Merge branch 'devel' into async-improvements

This commit is contained in:
Dominik Picheta 2018-01-17 16:28:00 +00:00
commit 47d05b3f2e
201 changed files with 6604 additions and 2628 deletions

View file

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

View file

@ -30,7 +30,7 @@ before_script:
- sh build.sh - sh build.sh
- cd .. - cd ..
- sed -i -e 's,cc = gcc,cc = clang,' config/nim.cfg - sed -i -e 's,cc = gcc,cc = clang,' config/nim.cfg
- export PATH=$(pwd)/bin:$PATH - export PATH=$(pwd)/bin${PATH:+:$PATH}
script: script:
- nim c koch - nim c koch
- ./koch boot - ./koch boot
@ -48,3 +48,5 @@ script:
- ./koch csource - ./koch csource
- ./koch nimsuggest - ./koch nimsuggest
# - nim c -r nimsuggest/tester # - nim c -r nimsuggest/tester
- ( ! grep -F '.. code-block' -l -r --include '*.html' --exclude contributing.html --exclude docgen.html --exclude tut2.html )
- ( ! grep -F '..code-block' -l -r --include '*.html' --exclude contributing.html --exclude docgen.html --exclude tut2.html )

View file

@ -53,36 +53,37 @@
what to return if the environment variable does not exist. what to return if the environment variable does not exist.
- Bodies of ``for`` loops now get their own scope: - Bodies of ``for`` loops now get their own scope:
.. code-block:: nim ```nim
# now compiles: # now compiles:
for i in 0..4: for i in 0..4:
let i = i + 1 let i = i + 1
echo i echo i
```
- The parsing rules of ``if`` expressions were changed so that multiple - The parsing rules of ``if`` expressions were changed so that multiple
statements are allowed in the branches. We found few code examples that statements are allowed in the branches. We found few code examples that
now fail because of this change, but here is one: now fail because of this change, but here is one:
.. code-block:: nim ```nim
t[ti] = if exp_negative: '-' else: '+'; inc(ti) t[ti] = if exp_negative: '-' else: '+'; inc(ti)
```
This now needs to be written as: This now needs to be written as:
.. code-block:: nim ```nim
t[ti] = (if exp_negative: '-' else: '+'); inc(ti) t[ti] = (if exp_negative: '-' else: '+'); inc(ti)
```
- To make Nim even more robust the system iterators ``..`` and ``countup`` - To make Nim even more robust the system iterators ``..`` and ``countup``
now only accept a single generic type ``T``. This means the following code now only accept a single generic type ``T``. This means the following code
doesn't die with an "out of range" error anymore: doesn't die with an "out of range" error anymore:
.. code-block:: nim ```nim
var b = 5.Natural var b = 5.Natural
var a = -5 var a = -5
for i in a..b: for i in a..b:
echo i echo i
```
- ``formatFloat``/``formatBiggestFloat`` now support formatting floats with zero - ``formatFloat``/``formatBiggestFloat`` now support formatting floats with zero
precision digits. The previous ``precision = 0`` behavior (default formatting) precision digits. The previous ``precision = 0`` behavior (default formatting)
@ -115,13 +116,17 @@ This now needs to be written as:
- Nim's ``rst2html`` command now supports the testing of code snippets via an RST - Nim's ``rst2html`` command now supports the testing of code snippets via an RST
extension that we called ``:test:``:: extension that we called ``:test:``::
```rst
.. code-block:: nim .. code-block:: nim
:test: :test:
# shows how the 'if' statement works # shows how the 'if' statement works
if true: echo "yes" if true: echo "yes"
```
- The ``[]`` proc for strings now raises an ``IndexError`` exception when - The ``[]`` proc for strings now raises an ``IndexError`` exception when
the specified slice is out of bounds. See issue the specified slice is out of bounds. See issue
[#6223](https://github.com/nim-lang/Nim/issues/6223) for more details. [#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 - ``strutils.split`` and ``strutils.rsplit`` with an empty string and a
separator now returns that empty string. separator now returns that empty string.
See issue [#4377](https://github.com/nim-lang/Nim/issues/4377). See issue [#4377](https://github.com/nim-lang/Nim/issues/4377).
@ -137,3 +142,68 @@ This now needs to be written as:
to [http://www.gii.upv.es/tlsf/](http://www.gii.upv.es/tlsf/) the maximum 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 fragmentation measured is lower than 25%. As a nice bonus ``alloc`` and
``dealloc`` became O(1) operations. ``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`.
Counting occurrences in a sequence used to be:
```nim
let mySeq = @[1, 2, 1, 3, 1, 4]
var myCounter = initCountTable[int]()
for item in mySeq:
myCounter.inc item
```
Now, you can simply do:
```nim
let
mySeq = @[1, 2, 1, 3, 1, 4]
myCounter = mySeq.toCountTable()
```
- 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.
- Pragmas now support call syntax, for example: ``{.exportc"myname".}`` and ``{.exportc("myname").}``
- Custom pragmas are now supported using pragma ``pragma``, please see language manual for details

View file

@ -6,7 +6,7 @@ cd csources
sh build.sh sh build.sh
cd .. cd ..
# Add Nim to the PATH # Add Nim to the PATH
export PATH=$(pwd)/bin:$PATH export PATH=$(pwd)/bin${PATH:+:$PATH}
# Bootstrap. # Bootstrap.
nim -v nim -v
nim c koch 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 gcc -v
export PATH=$(pwd)/bin:$PATH export PATH=$(pwd)/bin${PATH:+:$PATH}
# Nimble deps # Nimble deps
nim e install_nimble.nims nim e install_nimble.nims

View file

@ -49,7 +49,7 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
if a.sons[0] != nil: if a.sons[0] != nil:
result = isPartOfAux(a.sons[0].skipTypes(skipPtrs), b, marker) result = isPartOfAux(a.sons[0].skipTypes(skipPtrs), b, marker)
if result == arNo: result = isPartOfAux(a.n, b, marker) if result == arNo: result = isPartOfAux(a.n, b, marker)
of tyGenericInst, tyDistinct, tyAlias: of tyGenericInst, tyDistinct, tyAlias, tySink:
result = isPartOfAux(lastSon(a), b, marker) result = isPartOfAux(lastSon(a), b, marker)
of tyArray, tySet, tyTuple: of tyArray, tySet, tyTuple:
for i in countup(0, sonsLen(a) - 1): for i in countup(0, sonsLen(a) - 1):
@ -179,5 +179,11 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
result = isPartOf(a[0], b) result = isPartOf(a[0], b)
if result == arNo: result = arMaybe if result == arNo: result = arMaybe
else: discard else: discard
of nkObjConstr:
result = arNo
for i in 1..<b.len:
let res = isPartOf(a, b[i][1])
if res != arNo:
result = res
if res == arYes: break
else: discard else: discard

View file

@ -62,8 +62,8 @@ type
nkTripleStrLit, # a triple string literal """ nkTripleStrLit, # a triple string literal """
nkNilLit, # the nil literal nkNilLit, # the nil literal
# end of atoms # end of atoms
nkMetaNode_Obsolete, # difficult to explain; represents itself nkComesFrom, # "comes from" template/macro information for
# (used for macros) # better stack trace generation
nkDotCall, # used to temporarily flag a nkCall node; nkDotCall, # used to temporarily flag a nkCall node;
# this is used # this is used
# for transforming ``s.len`` to ``len(s)`` # for transforming ``s.len`` to ``len(s)``
@ -305,6 +305,7 @@ const
sfEscapes* = sfProcvar # param escapes sfEscapes* = sfProcvar # param escapes
sfBase* = sfDiscriminant sfBase* = sfDiscriminant
sfIsSelf* = sfOverriden # param is 'self' sfIsSelf* = sfOverriden # param is 'self'
sfCustomPragma* = sfRegister # symbol is custom pragma template
const const
# getting ready for the future expr/stmt merge # getting ready for the future expr/stmt merge
@ -354,7 +355,7 @@ type
tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers
tyFloat, tyFloat32, tyFloat64, tyFloat128, tyFloat, tyFloat32, tyFloat64, tyFloat128,
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64, tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64,
tyOptAsRef, tyUnused1, tyUnused2, tyOptAsRef, tySink, tyLent,
tyVarargs, tyVarargs,
tyUnused, tyUnused,
tyProxy # used as errornous type (for idetools) tyProxy # used as errornous type (for idetools)
@ -639,7 +640,8 @@ type
mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl,
mNHint, mNWarning, mNError, mNHint, mNWarning, mNError,
mInstantiationInfo, mGetTypeInfo, mNGenSym, mInstantiationInfo, mGetTypeInfo, mNGenSym,
mNimvm, mIntDefine, mStrDefine, mRunnableExamples mNimvm, mIntDefine, mStrDefine, mRunnableExamples,
mException, mBuiltinType
# things that we can evaluate safely at compile time, even if not asked for it: # things that we can evaluate safely at compile time, even if not asked for it:
const const
@ -939,13 +941,13 @@ const
tyGenericParam} tyGenericParam}
StructuralEquivTypes*: TTypeKinds = {tyNil, tyTuple, tyArray, StructuralEquivTypes*: TTypeKinds = {tyNil, tyTuple, tyArray,
tySet, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc, tyOpenArray, tySet, tyRange, tyPtr, tyRef, tyVar, tyLent, tySequence, tyProc, tyOpenArray,
tyVarargs} tyVarargs}
ConcreteTypes*: TTypeKinds = { # types of the expr that may occur in:: ConcreteTypes*: TTypeKinds = { # types of the expr that may occur in::
# var x = expr # var x = expr
tyBool, tyChar, tyEnum, tyArray, tyObject, tyBool, tyChar, tyEnum, tyArray, tyObject,
tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc, tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, tyLent, tySequence, tyProc,
tyPointer, tyPointer,
tyOpenArray, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128, tyOpenArray, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
tyUInt..tyUInt64} tyUInt..tyUInt64}
@ -1426,7 +1428,7 @@ proc propagateToOwner*(owner, elem: PType) =
owner.flags.incl tfHasMeta owner.flags.incl tfHasMeta
if tfHasAsgn in elem.flags: 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, if o2.kind in {tyTuple, tyObject, tyArray,
tySequence, tyOpt, tySet, tyDistinct}: tySequence, tyOpt, tySet, tyDistinct}:
o2.flags.incl tfHasAsgn o2.flags.incl tfHasAsgn
@ -1434,7 +1436,7 @@ proc propagateToOwner*(owner, elem: PType) =
if owner.kind notin {tyProc, tyGenericInst, tyGenericBody, if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
tyGenericInvocation, tyPtr}: tyGenericInvocation, tyPtr}:
let elemB = elem.skipTypes({tyGenericInst, tyAlias}) let elemB = elem.skipTypes({tyGenericInst, tyAlias, tySink})
if elemB.isGCedMem or tfHasGCedMem in elemB.flags: if elemB.isGCedMem or tfHasGCedMem in elemB.flags:
# for simplicity, we propagate this flag even to generics. We then # for simplicity, we propagate this flag even to generics. We then
# ensure this doesn't bite us in sempass2. # ensure this doesn't bite us in sempass2.

View file

@ -71,7 +71,7 @@ proc isInCurrentFrame(p: BProc, n: PNode): bool =
if n.sym.kind in {skVar, skResult, skTemp, skLet} and p.prc != nil: if n.sym.kind in {skVar, skResult, skTemp, skLet} and p.prc != nil:
result = p.prc.id == n.sym.owner.id result = p.prc.id == n.sym.owner.id
of nkDotExpr, nkBracketExpr: 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]) result = isInCurrentFrame(p, n.sons[0])
of nkHiddenStdConv, nkHiddenSubConv, nkConv: of nkHiddenStdConv, nkHiddenSubConv, nkConv:
result = isInCurrentFrame(p, n.sons[1]) result = isInCurrentFrame(p, n.sons[1])
@ -331,7 +331,7 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
# skip the deref: # skip the deref:
var ri = ri[i] var ri = ri[i]
while ri.kind == nkObjDownConv: ri = ri[0] 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: if t.kind == tyVar:
let x = if ri.kind == nkHiddenAddr: ri[0] else: ri let x = if ri.kind == nkHiddenAddr: ri[0] else: ri
if x.typ.kind == tyPtr: if x.typ.kind == tyPtr:
@ -527,7 +527,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
line(p, cpsStmts, pl) line(p, cpsStmts, pl)
proc genCall(p: BProc, e: PNode, d: var TLoc) = 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) genClosureCall(p, nil, e, d)
elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags: elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags:
genInfixCall(p, nil, e, d) genInfixCall(p, nil, e, d)
@ -538,7 +538,7 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) =
postStmtActions(p) postStmtActions(p)
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) = 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) genClosureCall(p, le, ri, d)
elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags: elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags:
genInfixCall(p, le, ri, d) genInfixCall(p, le, ri, d)

View file

@ -149,7 +149,7 @@ proc getStorageLoc(n: PNode): TStorageLoc =
else: result = OnUnknown else: result = OnUnknown
of nkDerefExpr, nkHiddenDeref: of nkDerefExpr, nkHiddenDeref:
case n.sons[0].typ.kind case n.sons[0].typ.kind
of tyVar: result = OnUnknown of tyVar, tyLent: result = OnUnknown
of tyPtr: result = OnStack of tyPtr: result = OnStack
of tyRef: result = OnHeap of tyRef: result = OnHeap
else: internalError(n.info, "getStorageLoc") else: internalError(n.info, "getStorageLoc")
@ -368,7 +368,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
else: else:
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString, of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString,
tyInt..tyUInt64, tyRange, tyVar: tyInt..tyUInt64, tyRange, tyVar, tyLent:
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
else: internalError("genAssignment: " & $ty.kind) else: internalError("genAssignment: " & $ty.kind)
@ -683,9 +683,10 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
d.storage = OnHeap d.storage = OnHeap
else: else:
var a: TLoc var a: TLoc
var typ = skipTypes(e.sons[0].typ, abstractInst) var typ = e.sons[0].typ
if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass: if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass:
typ = typ.lastSon typ = typ.lastSon
typ = typ.skipTypes(abstractInst)
if typ.kind == tyVar and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e.sons[0].kind == nkHiddenAddr: if typ.kind == tyVar and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e.sons[0].kind == nkHiddenAddr:
initLocExprSingleUse(p, e[0][0], d) initLocExprSingleUse(p, e[0][0], d)
return return
@ -849,7 +850,7 @@ proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
var a, b: TLoc var a, b: TLoc
initLocExpr(p, x, a) initLocExpr(p, x, a)
initLocExpr(p, y, b) initLocExpr(p, y, b)
var ty = skipTypes(skipTypes(a.t, abstractVarRange), abstractPtrs) var ty = skipTypes(a.t, abstractVarRange + abstractPtrs + tyUserTypeClasses)
var first = intLiteral(firstOrd(ty)) var first = intLiteral(firstOrd(ty))
# emit range check: # emit range check:
if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags: if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags:
@ -1201,18 +1202,30 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
# we skip this step here: # we skip this step here:
if not p.module.compileToCpp: if not p.module.compileToCpp:
if handleConstExpr(p, e, d): return if handleConstExpr(p, e, d): return
var tmp: TLoc
var t = e.typ.skipTypes(abstractInst) var t = e.typ.skipTypes(abstractInst)
getTemp(p, t, tmp)
let isRef = t.kind == tyRef let isRef = t.kind == tyRef
var r = rdLoc(tmp)
if isRef: # check if we need to construct the object in a temporary
rawGenNew(p, tmp, nil) var useTemp =
t = t.lastSon.skipTypes(abstractInst) isRef or
r = "(*$1)" % [r] (d.k notin {locTemp,locLocalVar,locGlobalVar,locParam,locField}) or
gcUsage(e) (isPartOf(d.lode, e) != arNo)
var tmp: TLoc
var r: Rope
if useTemp:
getTemp(p, t, tmp)
r = rdLoc(tmp)
if isRef:
rawGenNew(p, tmp, nil)
t = t.lastSon.skipTypes(abstractInst)
r = "(*$1)" % [r]
gcUsage(e)
else:
constructLoc(p, tmp)
else: else:
constructLoc(p, tmp) resetLoc(p, d)
r = rdLoc(d)
discard getTypeDesc(p.module, t) discard getTypeDesc(p.module, t)
let ty = getUniqueType(t) let ty = getUniqueType(t)
for i in 1 ..< e.len: for i in 1 ..< e.len:
@ -1226,28 +1239,46 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
genFieldCheck(p, it.sons[2], r, field) genFieldCheck(p, it.sons[2], r, field)
add(tmp2.r, ".") add(tmp2.r, ".")
add(tmp2.r, field.loc.r) add(tmp2.r, field.loc.r)
tmp2.k = locTemp if useTemp:
tmp2.k = locTemp
tmp2.storage = if isRef: OnHeap else: OnStack
else:
tmp2.k = d.k
tmp2.storage = if isRef: OnHeap else: d.storage
tmp2.lode = it.sons[1] tmp2.lode = it.sons[1]
tmp2.storage = if isRef: OnHeap else: OnStack
expr(p, it.sons[1], tmp2) expr(p, it.sons[1], tmp2)
if useTemp:
if d.k == locNone:
d = tmp
else:
genAssignment(p, d, tmp, {})
if d.k == locNone: proc lhsDoesAlias(a, b: PNode): bool =
d = tmp for y in b:
else: if isPartOf(a, y) != arNo: return true
genAssignment(p, d, tmp, {})
proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) = proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
var arr: TLoc var arr, tmp: TLoc
if d.k == locNone: # bug #668
let doesAlias = lhsDoesAlias(d.lode, n)
let dest = if doesAlias: addr(tmp) else: addr(d)
if doesAlias:
getTemp(p, n.typ, tmp)
elif d.k == locNone:
getTemp(p, n.typ, d) getTemp(p, n.typ, d)
# generate call to newSeq before adding the elements per hand: # generate call to newSeq before adding the elements per hand:
genNewSeqAux(p, d, intLiteral(sonsLen(n))) genNewSeqAux(p, dest[], intLiteral(sonsLen(n)))
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
initLoc(arr, locExpr, n[i], OnHeap) initLoc(arr, locExpr, n[i], OnHeap)
arr.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i)) arr.r = rfmt(nil, "$1->data[$2]", rdLoc(dest[]), intLiteral(i))
arr.storage = OnHeap # we know that sequences are on the heap arr.storage = OnHeap # we know that sequences are on the heap
expr(p, n[i], arr) expr(p, n[i], arr)
gcUsage(n) gcUsage(n)
if doesAlias:
if d.k == locNone:
d = tmp
else:
genAssignment(p, d, tmp, {})
proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
var elem, a, arr: TLoc var elem, a, arr: TLoc
@ -1692,7 +1723,7 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) =
rope(magic)]), a.storage) rope(magic)]), a.storage)
proc genConv(p: BProc, e: PNode, d: var TLoc) = proc genConv(p: BProc, e: PNode, d: var TLoc) =
let destType = e.typ.skipTypes({tyVar, tyGenericInst, tyAlias}) let destType = e.typ.skipTypes({tyVar, tyGenericInst, tyAlias, tySink})
if sameBackendType(destType, e.sons[1].typ): if sameBackendType(destType, e.sons[1].typ):
expr(p, e.sons[1], d) expr(p, e.sons[1], d)
else: else:
@ -1768,7 +1799,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
"$# = #subInt64($#, $#);$n"] "$# = #subInt64($#, $#);$n"]
const fun: array[mInc..mDec, string] = ["$# = #addInt($#, $#);$n", const fun: array[mInc..mDec, string] = ["$# = #addInt($#, $#);$n",
"$# = #subInt($#, $#);$n"] "$# = #subInt($#, $#);$n"]
let underlying = skipTypes(e.sons[1].typ, {tyGenericInst, tyAlias, tyVar, tyRange}) let underlying = skipTypes(e.sons[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyRange})
if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}: if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}:
binaryStmt(p, e, d, opr[op]) binaryStmt(p, e, d, opr[op])
else: else:
@ -1778,7 +1809,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b) initLocExpr(p, e.sons[2], b)
let ranged = skipTypes(e.sons[1].typ, {tyGenericInst, tyAlias, tyVar}) let ranged = skipTypes(e.sons[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent})
let res = binaryArithOverflowRaw(p, ranged, a, b, let res = binaryArithOverflowRaw(p, ranged, a, b,
if underlying.kind == tyInt64: fun64[op] else: fun[op]) if underlying.kind == tyInt64: fun64[op] else: fun[op])
putIntoDest(p, a, e.sons[1], "($#)($#)" % [ putIntoDest(p, a, e.sons[1], "($#)($#)" % [
@ -1968,10 +1999,35 @@ proc genComplexConst(p: BProc, sym: PSym, d: var TLoc) =
assert((sym.loc.r != nil) and (sym.loc.t != nil)) assert((sym.loc.r != nil) and (sym.loc.t != nil))
putLocIntoDest(p, d, sym.loc) putLocIntoDest(p, d, sym.loc)
template genStmtListExprImpl(exprOrStmt) {.dirty.} =
#let hasNimFrame = magicsys.getCompilerProc("nimFrame") != nil
let hasNimFrame = p.prc != nil and
sfSystemModule notin p.module.module.flags and
optStackTrace in p.prc.options
var frameName: Rope = nil
for i in 0 .. n.len - 2:
let it = n[i]
if it.kind == nkComesFrom:
if hasNimFrame and frameName == nil:
inc p.labels
frameName = "FR" & rope(p.labels) & "_"
let theMacro = it[0].sym
add p.s(cpsStmts), initFrameNoDebug(p, frameName,
makeCString theMacro.name.s,
theMacro.info.quotedFilename, it.info.line)
else:
genStmts(p, it)
if n.len > 0: exprOrStmt
if frameName != nil:
add p.s(cpsStmts), deinitFrameNoDebug(p, frameName)
proc genStmtListExpr(p: BProc, n: PNode, d: var TLoc) = proc genStmtListExpr(p: BProc, n: PNode, d: var TLoc) =
var length = sonsLen(n) genStmtListExprImpl:
for i in countup(0, length - 2): genStmts(p, n.sons[i]) expr(p, n[n.len - 1], d)
if length > 0: expr(p, n.sons[length - 1], d)
proc genStmtList(p: BProc, n: PNode) =
genStmtListExprImpl:
genStmts(p, n[n.len - 1])
proc upConv(p: BProc, n: PNode, d: var TLoc) = proc upConv(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc var a: TLoc
@ -1981,9 +2037,9 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
var r = rdLoc(a) var r = rdLoc(a)
var nilCheck: Rope = nil var nilCheck: Rope = nil
var t = skipTypes(a.t, abstractInst) var t = skipTypes(a.t, abstractInst)
while t.kind in {tyVar, tyPtr, tyRef}: while t.kind in {tyVar, tyLent, tyPtr, tyRef}:
if t.kind != tyVar: nilCheck = r if t.kind notin {tyVar, tyLent}: nilCheck = r
if t.kind != tyVar or not p.module.compileToCpp: if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp:
r = "(*$1)" % [r] r = "(*$1)" % [r]
t = skipTypes(t.lastSon, abstractInst) t = skipTypes(t.lastSon, abstractInst)
if not p.module.compileToCpp: if not p.module.compileToCpp:
@ -2016,7 +2072,7 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc var a: TLoc
initLocExpr(p, arg, a) initLocExpr(p, arg, a)
var r = rdLoc(a) var r = rdLoc(a)
let isRef = skipTypes(arg.typ, abstractInst).kind in {tyRef, tyPtr, tyVar} let isRef = skipTypes(arg.typ, abstractInst).kind in {tyRef, tyPtr, tyVar, tyLent}
if isRef: if isRef:
add(r, "->Sup") add(r, "->Sup")
else: else:
@ -2028,7 +2084,7 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) =
# (see bug #837). However sometimes using a temporary is not correct: # (see bug #837). However sometimes using a temporary is not correct:
# init(TFigure(my)) # where it is passed to a 'var TFigure'. We test # init(TFigure(my)) # where it is passed to a 'var TFigure'. We test
# this by ensuring the destination is also a pointer: # this by ensuring the destination is also a pointer:
if d.k == locNone and skipTypes(n.typ, abstractInst).kind in {tyRef, tyPtr, tyVar}: if d.k == locNone and skipTypes(n.typ, abstractInst).kind in {tyRef, tyPtr, tyVar, tyLent}:
getTemp(p, n.typ, d) getTemp(p, n.typ, d)
linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r) linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r)
else: else:
@ -2170,8 +2226,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of nkCheckedFieldExpr: genCheckedRecordField(p, n, d) of nkCheckedFieldExpr: genCheckedRecordField(p, n, d)
of nkBlockExpr, nkBlockStmt: genBlock(p, n, d) of nkBlockExpr, nkBlockStmt: genBlock(p, n, d)
of nkStmtListExpr: genStmtListExpr(p, n, d) of nkStmtListExpr: genStmtListExpr(p, n, d)
of nkStmtList: of nkStmtList: genStmtList(p, n)
for i in countup(0, sonsLen(n) - 1): genStmts(p, n.sons[i])
of nkIfExpr, nkIfStmt: genIf(p, n, d) of nkIfExpr, nkIfStmt: genIf(p, n, d)
of nkWhen: of nkWhen:
# This should be a "when nimvm" node. # This should be a "when nimvm" node.
@ -2268,7 +2323,7 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope =
of tyBool: result = rope"NIM_FALSE" of tyBool: result = rope"NIM_FALSE"
of tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64: result = rope"0" of tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64: result = rope"0"
of tyFloat..tyFloat128: result = rope"0.0" of tyFloat..tyFloat128: result = rope"0.0"
of tyCString, tyString, tyVar, tyPointer, tyPtr, tySequence, tyExpr, of tyCString, tyString, tyVar, tyLent, tyPointer, tyPtr, tySequence, tyExpr,
tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil: tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil:
result = rope"NIM_NIL" result = rope"NIM_NIL"
of tyProc: of tyProc:

View file

@ -96,7 +96,7 @@ proc writeIntSet(a: IntSet, s: var string) =
s.add('}') s.add('}')
proc genMergeInfo*(m: BModule): Rope = proc genMergeInfo*(m: BModule): Rope =
if optSymbolFiles notin gGlobalOptions: return nil if not compilationCachePresent: return nil
var s = "/*\tNIM_merge_INFO:" var s = "/*\tNIM_merge_INFO:"
s.add(tnl) s.add(tnl)
s.add("typeCache:{") s.add("typeCache:{")

View file

@ -21,8 +21,12 @@ proc registerGcRoot(p: BProc, v: PSym) =
# we register a specialized marked proc here; this has the advantage # we register a specialized marked proc here; this has the advantage
# that it works out of the box for thread local storage then :-) # that it works out of the box for thread local storage then :-)
let prc = genTraverseProcForGlobal(p.module, v, v.info) let prc = genTraverseProcForGlobal(p.module, v, v.info)
appcg(p.module, p.module.initProc.procSec(cpsInit), if sfThread in v.flags:
"#nimRegisterGlobalMarker($1);$n", [prc]) 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.} = proc isAssignedImmediately(n: PNode): bool {.inline.} =
if n.kind == nkEmpty: return false if n.kind == nkEmpty: return false
@ -564,9 +568,6 @@ proc genBreakStmt(p: BProc, t: PNode) =
genLineDir(p, t) genLineDir(p, t)
lineF(p, cpsStmts, "goto $1;$n", [label]) lineF(p, cpsStmts, "goto $1;$n", [label])
proc getRaiseFrmt(p: BProc): string =
result = "#raiseException((#Exception*)$1, $2);$n"
proc genRaiseStmt(p: BProc, t: PNode) = proc genRaiseStmt(p: BProc, t: PNode) =
if p.inExceptBlock > 0: if p.inExceptBlock > 0:
# if the current try stmt have a finally block, # if the current try stmt have a finally block,
@ -580,7 +581,8 @@ proc genRaiseStmt(p: BProc, t: PNode) =
var e = rdLoc(a) var e = rdLoc(a)
var typ = skipTypes(t.sons[0].typ, abstractPtrs) var typ = skipTypes(t.sons[0].typ, abstractPtrs)
genLineDir(p, t) genLineDir(p, t)
lineCg(p, cpsStmts, getRaiseFrmt(p), [e, makeCString(typ.sym.name.s)]) lineCg(p, cpsStmts, "#raiseException((#Exception*)$1, $2);$n",
[e, makeCString(typ.sym.name.s)])
else: else:
genLineDir(p, t) genLineDir(p, t)
# reraise the last exception: # reraise the last exception:

View file

@ -66,7 +66,8 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType) =
var p = c.p var p = c.p
case typ.kind case typ.kind
of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred: of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
tySink:
genTraverseProc(c, accessor, lastSon(typ)) genTraverseProc(c, accessor, lastSon(typ))
of tyArray: of tyArray:
let arraySize = lengthOrd(typ.sons[0]) let arraySize = lengthOrd(typ.sons[0])

View file

@ -119,7 +119,7 @@ proc scopeMangledParam(p: BProc; param: PSym) =
const const
irrelevantForBackend = {tyGenericBody, tyGenericInst, tyGenericInvocation, irrelevantForBackend = {tyGenericBody, tyGenericInst, tyGenericInvocation,
tyDistinct, tyRange, tyStatic, tyAlias, tyInferred} tyDistinct, tyRange, tyStatic, tyAlias, tySink, tyInferred}
proc typeName(typ: PType): Rope = proc typeName(typ: PType): Rope =
let typ = typ.skipTypes(irrelevantForBackend) let typ = typ.skipTypes(irrelevantForBackend)
@ -139,7 +139,7 @@ proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
t = t.lastSon t = t.lastSon
else: else:
break 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: if typ.loc.r == nil:
typ.loc.r = typ.typeName & $sig typ.loc.r = typ.typeName & $sig
else: else:
@ -170,7 +170,7 @@ proc mapType(typ: PType): TCTypeKind =
internalAssert typ.isResolvedUserTypeClass internalAssert typ.isResolvedUserTypeClass
return mapType(typ.lastSon) return mapType(typ.lastSon)
of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal, of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal,
tyTypeDesc, tyAlias, tyInferred: tyTypeDesc, tyAlias, tySink, tyInferred:
result = mapType(lastSon(typ)) result = mapType(lastSon(typ))
of tyEnum: of tyEnum:
if firstOrd(typ) < 0: if firstOrd(typ) < 0:
@ -183,7 +183,7 @@ proc mapType(typ: PType): TCTypeKind =
of 8: result = ctInt64 of 8: result = ctInt64
else: internalError("mapType") else: internalError("mapType")
of tyRange: result = mapType(typ.sons[0]) of tyRange: result = mapType(typ.sons[0])
of tyPtr, tyVar, tyRef, tyOptAsRef: of tyPtr, tyVar, tyLent, tyRef, tyOptAsRef:
var base = skipTypes(typ.lastSon, typedescInst) var base = skipTypes(typ.lastSon, typedescInst)
case base.kind case base.kind
of tyOpenArray, tyArray, tyVarargs: result = ctPtrToArray of tyOpenArray, tyArray, tyVarargs: result = ctPtrToArray
@ -242,7 +242,7 @@ proc isInvalidReturnType(rettype: PType): bool =
case mapType(rettype) case mapType(rettype)
of ctArray: of ctArray:
result = not (skipTypes(rettype, typedescInst).kind in result = not (skipTypes(rettype, typedescInst).kind in
{tyVar, tyRef, tyPtr}) {tyVar, tyLent, tyRef, tyPtr})
of ctStruct: of ctStruct:
let t = skipTypes(rettype, typedescInst) let t = skipTypes(rettype, typedescInst)
if rettype.isImportedCppType or t.isImportedCppType: return false if rettype.isImportedCppType or t.isImportedCppType: return false
@ -328,7 +328,7 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
of tyStatic: of tyStatic:
if typ.n != nil: result = getSimpleTypeDesc(m, lastSon typ) if typ.n != nil: result = getSimpleTypeDesc(m, lastSon typ)
else: internalError("tyStatic for getSimpleTypeDesc") else: internalError("tyStatic for getSimpleTypeDesc")
of tyGenericInst, tyAlias: of tyGenericInst, tyAlias, tySink:
result = getSimpleTypeDesc(m, lastSon typ) result = getSimpleTypeDesc(m, lastSon typ)
else: result = nil else: result = nil
@ -348,7 +348,7 @@ proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope =
if result == nil: result = cacheGetType(m.typeCache, sig) if result == nil: result = cacheGetType(m.typeCache, sig)
proc structOrUnion(t: PType): Rope = 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")) (if tfUnion in t.flags: rope("union") else: rope("struct"))
proc getForwardStructFormat(m: BModule): string = proc getForwardStructFormat(m: BModule): string =
@ -396,7 +396,7 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
result = getTypeDescAux(m, t, check) result = getTypeDescAux(m, t, check)
proc paramStorageLoc(param: PSym): TStorageLoc = 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}: tyArray, tyOpenArray, tyVarargs}:
result = OnStack result = OnStack
else: else:
@ -430,11 +430,11 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
add(params, param.loc.r) add(params, param.loc.r)
# declare the len field for open arrays: # declare the len field for open arrays:
var arr = param.typ var arr = param.typ
if arr.kind == tyVar: arr = arr.sons[0] if arr.kind in {tyVar, tyLent}: arr = arr.lastSon
var j = 0 var j = 0
while arr.kind in {tyOpenArray, tyVarargs}: while arr.kind in {tyOpenArray, tyVarargs}:
# this fixes the 'sort' bug: # 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: # need to pass hidden parameter:
addf(params, ", NI $1Len_$2", [param.loc.r, j.rope]) addf(params, ", NI $1Len_$2", [param.loc.r, j.rope])
inc(j) inc(j)
@ -496,7 +496,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
if hasAttribute in CC[cCompiler].props: if hasAttribute in CC[cCompiler].props:
add(unionBody, "struct __attribute__((__packed__)){" ) add(unionBody, "struct __attribute__((__packed__)){" )
else: else:
addf(unionBody, "#pragma pack(1)$nstruct{", []) addf(unionBody, "#pragma pack(push, 1)$nstruct{", [])
add(unionBody, a) add(unionBody, a)
addf(unionBody, "} $1;$n", [sname]) addf(unionBody, "} $1;$n", [sname])
if tfPacked in rectype.flags and hasAttribute notin CC[cCompiler].props: if tfPacked in rectype.flags and hasAttribute notin CC[cCompiler].props:
@ -551,7 +551,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
if hasAttribute in CC[cCompiler].props: if hasAttribute in CC[cCompiler].props:
result = structOrUnion(typ) & " __attribute__((__packed__))" result = structOrUnion(typ) & " __attribute__((__packed__))"
else: else:
result = "#pragma pack(1)" & tnl & structOrUnion(typ) result = "#pragma pack(push, 1)" & tnl & structOrUnion(typ)
else: else:
result = structOrUnion(typ) result = structOrUnion(typ)
@ -641,7 +641,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
excl(check, t.id) excl(check, t.id)
return return
case t.kind 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 var star = if t.kind == tyVar and tfVarIsPtr notin origTyp.flags and
compileToCpp(m): "&" else: "*" compileToCpp(m): "&" else: "*"
var et = origTyp.skipTypes(abstractInst).lastSon var et = origTyp.skipTypes(abstractInst).lastSon
@ -872,7 +872,7 @@ 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)]) 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", else: addf(m.s[cfsTypes], "typedef NU8 $1[$2];$n",
[result, rope(getSize(t))]) [result, rope(getSize(t))])
of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink,
tyUserTypeClass, tyUserTypeClassInst, tyInferred: tyUserTypeClass, tyUserTypeClassInst, tyInferred:
result = getTypeDescAux(m, lastSon(t), check) result = getTypeDescAux(m, lastSon(t), check)
else: else:
@ -925,6 +925,8 @@ proc genProcHeader(m: BModule, prc: PSym): Rope =
result.add "N_LIB_EXPORT " result.add "N_LIB_EXPORT "
elif prc.typ.callConv == ccInline: elif prc.typ.callConv == ccInline:
result.add "static " result.add "static "
elif {sfImportc, sfExportc} * prc.flags == {}:
result.add "N_LIB_PRIVATE "
var check = initIntSet() var check = initIntSet()
fillLoc(prc.loc, locProc, prc.ast[namePos], mangleName(m, prc), OnUnknown) fillLoc(prc.loc, locProc, prc.ast[namePos], mangleName(m, prc), OnUnknown)
genProcParams(m, prc.typ, rettype, params, check) genProcParams(m, prc.typ, rettype, params, check)
@ -1225,7 +1227,7 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
m.g.typeInfoMarker[sig] = result m.g.typeInfoMarker[sig] = result
case t.kind case t.kind
of tyEmpty, tyVoid: result = rope"0" 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) genTypeInfoAuxBase(m, t, t, result, rope"0", info)
of tyStatic: of tyStatic:
if t.n != nil: result = genTypeInfo(m, lastSon t, info) if t.n != nil: result = genTypeInfo(m, lastSon t, info)

View file

@ -110,13 +110,13 @@ proc getUniqueType*(key: PType): PType =
of tyDistinct: of tyDistinct:
if key.deepCopy != nil: result = key if key.deepCopy != nil: result = key
else: result = getUniqueType(lastSon(key)) else: result = getUniqueType(lastSon(key))
of tyGenericInst, tyOrdinal, tyStatic, tyAlias, tyInferred: of tyGenericInst, tyOrdinal, tyStatic, tyAlias, tySink, tyInferred:
result = getUniqueType(lastSon(key)) result = getUniqueType(lastSon(key))
#let obj = lastSon(key) #let obj = lastSon(key)
#if obj.sym != nil and obj.sym.name.s == "TOption": #if obj.sym != nil and obj.sym.name.s == "TOption":
# echo "for ", typeToString(key), " I returned " # echo "for ", typeToString(key), " I returned "
# debug result # debug result
of tyPtr, tyRef, tyVar: of tyPtr, tyRef, tyVar, tyLent:
let elemType = lastSon(key) let elemType = lastSon(key)
if elemType.kind in {tyBool, tyChar, tyInt..tyUInt64}: if elemType.kind in {tyBool, tyChar, tyInt..tyUInt64}:
# no canonicalization for integral types, so that e.g. ``ptr pid_t`` is # no canonicalization for integral types, so that e.g. ``ptr pid_t`` is

View file

@ -493,7 +493,32 @@ proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) =
proc lenField(p: BProc): Rope = proc lenField(p: BProc): Rope =
result = rope(if p.module.compileToCpp: "len" else: "Sup.len") result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
include ccgcalls, "ccgstmts.nim", "ccgexprs.nim" include ccgcalls, "ccgstmts.nim"
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",
procname, filename, p.maxFrameLen.rope,
p.blocks[0].frameLen.rope)
else:
result = rfmt(nil, "\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; " &
" $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)
proc deinitFrame(p: BProc): Rope =
result = rfmt(p.module, "\t#popFrame();$n")
include ccgexprs
# ----------------------------- dynamic library handling ----------------- # ----------------------------- dynamic library handling -----------------
# We don't finalize dynamic libs as the OS does this for us. # We don't finalize dynamic libs as the OS does this for us.
@ -600,7 +625,7 @@ proc symInDynamicLibPartial(m: BModule, sym: PSym) =
sym.typ.sym = nil # generate a new name sym.typ.sym = nil # generate a new name
proc cgsym(m: BModule, name: string): Rope = proc cgsym(m: BModule, name: string): Rope =
var sym = magicsys.getCompilerProc(name) let sym = magicsys.getCompilerProc(name)
if sym != nil: if sym != nil:
case sym.kind case sym.kind
of skProc, skFunc, skMethod, skConverter, skIterator: genProc(m, sym) of skProc, skFunc, skMethod, skConverter, skIterator: genProc(m, sym)
@ -637,19 +662,6 @@ proc generateHeaders(m: BModule) =
add(m.s[cfsHeaders], "#undef powerpc" & tnl) add(m.s[cfsHeaders], "#undef powerpc" & tnl)
add(m.s[cfsHeaders], "#undef unix" & tnl) add(m.s[cfsHeaders], "#undef unix" & tnl)
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",
procname, filename, p.maxFrameLen.rope,
p.blocks[0].frameLen.rope)
else:
result = rfmt(nil, "\tnimfr_($1, $2);$n", procname, filename)
proc deinitFrame(p: BProc): Rope =
result = rfmt(p.module, "\t#popFrame();$n")
proc closureSetup(p: BProc, prc: PSym) = proc closureSetup(p: BProc, prc: PSym) =
if tfCapturesEnv notin prc.typ.flags: return if tfCapturesEnv notin prc.typ.flags: return
# prc.ast[paramsPos].last contains the type we're after: # prc.ast[paramsPos].last contains the type we're after:
@ -1246,7 +1258,7 @@ proc resetModule*(m: BModule) =
# indicate that this is now cached module # indicate that this is now cached module
# the cache will be invalidated by nullifying gModules # the cache will be invalidated by nullifying gModules
m.fromCache = true #m.fromCache = true
m.g = nil m.g = nil
# we keep only the "merge info" information for the module # we keep only the "merge info" information for the module
@ -1324,7 +1336,6 @@ proc getCFile(m: BModule): string =
proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext = proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext =
injectG(graph.config) injectG(graph.config)
assert optSymbolFiles in gGlobalOptions
var m = newModule(g, module) var m = newModule(g, module)
readMergeInfo(getCFile(m), m) readMergeInfo(getCFile(m), m)
result = m result = m
@ -1378,7 +1389,7 @@ proc writeModule(m: BModule, pending: bool) =
# generate code for the init statements of the module: # generate code for the init statements of the module:
let cfile = getCFile(m) let cfile = getCFile(m)
if not m.fromCache or optForceFullMake in gGlobalOptions: if m.rd == nil or optForceFullMake in gGlobalOptions:
genInitCode(m) genInitCode(m)
finishTypeDescriptions(m) finishTypeDescriptions(m)
if sfMainModule in m.module.flags: if sfMainModule in m.module.flags:
@ -1431,6 +1442,10 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
result = n result = n
if b == nil or passes.skipCodegen(n): return if b == nil or passes.skipCodegen(n): return
var m = BModule(b) var m = BModule(b)
# 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?
if b.rd != nil: return
if n != nil: if n != nil:
m.initProc.options = initProcOptions(m) m.initProc.options = initProcOptions(m)
genStmts(m.initProc, n) genStmts(m.initProc, n)
@ -1453,10 +1468,10 @@ proc cgenWriteModules*(backend: RootRef, config: ConfigRef) =
if g.generatedHeader != nil: finishModule(g.generatedHeader) if g.generatedHeader != nil: finishModule(g.generatedHeader)
while g.forwardedProcsCounter > 0: while g.forwardedProcsCounter > 0:
for m in cgenModules(g): for m in cgenModules(g):
if not m.fromCache: if m.rd == nil:
finishModule(m) finishModule(m)
for m in cgenModules(g): for m in cgenModules(g):
if m.fromCache: if m.rd != nil:
m.updateCachedModule m.updateCachedModule
else: else:
m.writeModule(pending=true) m.writeModule(pending=true)

View file

@ -68,7 +68,7 @@ proc sameMethodBucket(a, b: PSym): MethodResult =
while true: while true:
aa = skipTypes(aa, {tyGenericInst, tyAlias}) aa = skipTypes(aa, {tyGenericInst, tyAlias})
bb = skipTypes(bb, {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 aa = aa.lastSon
bb = bb.lastSon bb = bb.lastSon
else: else:

View file

@ -261,7 +261,7 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
of "assertions", "a": result = contains(gOptions, optAssert) of "assertions", "a": result = contains(gOptions, optAssert)
of "deadcodeelim": result = contains(gGlobalOptions, optDeadCodeElim) of "deadcodeelim": result = contains(gGlobalOptions, optDeadCodeElim)
of "run", "r": result = contains(gGlobalOptions, optRun) of "run", "r": result = contains(gGlobalOptions, optRun)
of "symbolfiles": result = contains(gGlobalOptions, optSymbolFiles) of "symbolfiles": result = gSymbolFiles != disabledSf
of "genscript": result = contains(gGlobalOptions, optGenScript) of "genscript": result = contains(gGlobalOptions, optGenScript)
of "threads": result = contains(gGlobalOptions, optThreads) of "threads": result = contains(gGlobalOptions, optThreads)
of "taintmode": result = contains(gGlobalOptions, optTaintMode) of "taintmode": result = contains(gGlobalOptions, optTaintMode)
@ -598,7 +598,13 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
helpOnError(pass) helpOnError(pass)
of "symbolfiles": of "symbolfiles":
processOnOffSwitchG({optSymbolFiles}, arg, pass, info) case arg.normalize
of "on": gSymbolFiles = enabledSf
of "off": gSymbolFiles = disabledSf
of "writeonly": gSymbolFiles = writeOnlySf
of "readonly": gSymbolFiles = readOnlySf
of "v2": gSymbolFiles = v2Sf
else: localError(info, errOnOrOffExpectedButXFound, arg)
of "skipcfg": of "skipcfg":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optSkipConfigFile) incl(gGlobalOptions, optSkipConfigFile)
@ -611,7 +617,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "skipparentcfg": of "skipparentcfg":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optSkipParentConfigFiles) incl(gGlobalOptions, optSkipParentConfigFiles)
of "genscript": of "genscript", "gendeps":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
incl(gGlobalOptions, optGenScript) incl(gGlobalOptions, optGenScript)
of "colors": processOnOffSwitchG({optUseColors}, arg, pass, info) of "colors": processOnOffSwitchG({optUseColors}, arg, pass, info)

View file

@ -112,3 +112,4 @@ proc initDefines*() =
defineSymbol("nimNewRoof") defineSymbol("nimNewRoof")
defineSymbol("nimHasRunnableExamples") defineSymbol("nimHasRunnableExamples")
defineSymbol("nimNewDot") defineSymbol("nimNewDot")
defineSymbol("nimHasNilChecks")

View file

@ -174,7 +174,7 @@ proc patchHead(n: PNode) =
if n[1].typ.isNil: if n[1].typ.isNil:
# XXX toptree crashes without this workaround. Figure out why. # XXX toptree crashes without this workaround. Figure out why.
return 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) = template patch(op, field) =
if s.name.s == op and field != nil and field != s: if s.name.s == op and field != nil and field != s:
n.sons[0].sym = field n.sons[0].sym = field
@ -198,15 +198,15 @@ template genOp(opr, opname) =
result = newTree(nkCall, newSymNode(op), newTree(nkHiddenAddr, dest)) result = newTree(nkCall, newSymNode(op), newTree(nkHiddenAddr, dest))
proc genSink(t: PType; dest: PNode): PNode = proc genSink(t: PType; dest: PNode): PNode =
let t = t.skipTypes({tyGenericInst, tyAlias}) let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
genOp(if t.sink != nil: t.sink else: t.assignment, "=sink") genOp(if t.sink != nil: t.sink else: t.assignment, "=sink")
proc genCopy(t: PType; dest: PNode): PNode = proc genCopy(t: PType; dest: PNode): PNode =
let t = t.skipTypes({tyGenericInst, tyAlias}) let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
genOp(t.assignment, "=") genOp(t.assignment, "=")
proc genDestroy(t: PType; dest: PNode): PNode = proc genDestroy(t: PType; dest: PNode): PNode =
let t = t.skipTypes({tyGenericInst, tyAlias}) let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
genOp(t.destructor, "=destroy") genOp(t.destructor, "=destroy")
proc addTopVar(c: var Con; v: PNode) = proc addTopVar(c: var Con; v: PNode) =
@ -296,7 +296,8 @@ proc p(n: PNode; c: var Con): PNode =
recurse(n, result) recurse(n, result)
proc injectDestructorCalls*(owner: PSym; n: PNode): PNode = proc injectDestructorCalls*(owner: PSym; n: PNode): PNode =
echo "injecting into ", n when defined(nimDebugDestroys):
echo "injecting into ", n
var c: Con var c: Con
c.owner = owner c.owner = owner
c.tmp = newSym(skTemp, getIdent":d", owner, n.info) c.tmp = newSym(skTemp, getIdent":d", owner, n.info)

View file

@ -132,7 +132,7 @@ proc gen(c: var Con; n: PNode) # {.noSideEffect.}
proc genWhile(c: var Con; n: PNode) = proc genWhile(c: var Con; n: PNode) =
# L1: # L1:
# cond, tmp # cond, tmp
# fjmp tmp, L2 # fork tmp, L2
# body # body
# jmp L1 # jmp L1
# L2: # L2:
@ -168,15 +168,13 @@ proc genIf(c: var Con, n: PNode) =
var endings: seq[TPosition] = @[] var endings: seq[TPosition] = @[]
for i in countup(0, len(n) - 1): for i in countup(0, len(n) - 1):
var it = n.sons[i] var it = n.sons[i]
c.gen(it.sons[0])
if it.len == 2: if it.len == 2:
c.gen(it.sons[0].sons[1]) let elsePos = c.forkI(it.sons[1])
var elsePos = c.forkI(it.sons[0].sons[1])
c.gen(it.sons[1]) c.gen(it.sons[1])
if i < sonsLen(n)-1: if i < sonsLen(n)-1:
endings.add(c.gotoI(it.sons[1])) endings.add(c.gotoI(it.sons[1]))
c.patch(elsePos) c.patch(elsePos)
else:
c.gen(it.sons[0])
for endPos in endings: c.patch(endPos) for endPos in endings: c.patch(endPos)
proc genAndOr(c: var Con; n: PNode) = proc genAndOr(c: var Con; n: PNode) =
@ -337,100 +335,107 @@ proc gen(c: var Con; n: PNode) =
else: discard else: discard
proc dfa(code: seq[Instr]) = proc dfa(code: seq[Instr]) =
# We aggressively push 'undef' values for every 'use v' instruction var u = newSeq[IntSet](code.len) # usages
# until they are eliminated via a 'def v' instructions. var d = newSeq[IntSet](code.len) # defs
# If we manage to push one 'undef' to a 'use' instruction, we produce var c = newSeq[IntSet](code.len) # consumed
# an error: var backrefs = initTable[int, int]()
var undef = initIntSet()
for i in 0..<code.len: for i in 0..<code.len:
if code[i].kind == use: undef.incl(code[i].sym.id) u[i] = initIntSet()
d[i] = initIntSet()
c[i] = initIntSet()
case code[i].kind
of use, useWithinCall: u[i].incl(code[i].sym.id)
of def: d[i].incl(code[i].sym.id)
of fork, goto:
let d = i+code[i].dest
backrefs.add(d, i)
var s = newSeq[IntSet](code.len)
for i in 0..<code.len:
assign(s[i], undef)
# In the original paper, W := {0,...,n} is done. This is wasteful, we
# have no intention to analyse a program like
#
# return 3
# echo a + b
#
# any further than necessary.
var w = @[0] var w = @[0]
while w.len > 0: var maxIters = 50
var pc = w[^1] var someChange = true
var takenGotos = initIntSet()
var consuming = -1
while w.len > 0 and maxIters > 0: # and someChange:
dec maxIters
var pc = w.pop() # w[^1]
var prevPc = -1
# this simulates a single linear control flow execution: # this simulates a single linear control flow execution:
while true: while pc < code.len:
# according to the paper, it is better to shrink the working set here if prevPc >= 0:
# in this inner loop: someChange = false
let widx = w.find(pc) # merge step and test for changes (we compute the fixpoints here):
if widx >= 0: w.del(widx) # 'u' needs to be the union of prevPc, pc
# 'd' needs to be the intersection of 'pc'
for id in u[prevPc]:
if not u[pc].containsOrIncl(id):
someChange = true
# in (a; b) if ``a`` sets ``v`` so does ``b``. The intersection
# is only interesting on merge points:
for id in d[prevPc]:
if not d[pc].containsOrIncl(id):
someChange = true
# if this is a merge point, we take the intersection of the 'd' sets:
if backrefs.hasKey(pc):
var intersect = initIntSet()
assign(intersect, d[pc])
var first = true
for prevPc in backrefs.allValues(pc):
for def in d[pc]:
if def notin d[prevPc]:
excl(intersect, def)
someChange = true
when defined(debugDfa):
echo "Excluding ", pc, " prev ", prevPc
assign d[pc], intersect
if consuming >= 0:
if not c[pc].containsOrIncl(consuming):
someChange = true
consuming = -1
# our interpretation ![I!]: # our interpretation ![I!]:
var sid = -1 prevPc = pc
case code[pc].kind case code[pc].kind
of goto, fork: discard
of use, useWithinCall:
let sym = code[pc].sym
if s[pc].contains(sym.id):
localError(code[pc].n.info, "variable read before initialized: " & sym.name.s)
of def:
sid = code[pc].sym.id
var pc2: int
if code[pc].kind == goto:
pc2 = pc + code[pc].dest
else:
pc2 = pc + 1
if code[pc].kind == fork:
let l = pc + code[pc].dest
if sid >= 0 and s[l].missingOrExcl(sid):
w.add l
if sid >= 0 and s[pc2].missingOrExcl(sid):
pc = pc2
else:
break
if pc >= code.len: break
when false:
case code[pc].kind
of use:
let s = code[pc].sym
if undefB.contains(s.id):
localError(code[pc].n.info, "variable read before initialized: " & s.name.s)
break
inc pc
of def:
let s = code[pc].sym
# exclude 'undef' for s for this path through the graph.
if not undefB.missingOrExcl(s.id):
inc pc
else:
break
#undefB.excl s.id
#inc pc
when false:
let prev = bindings.getOrDefault(s.id)
if prev != value:
# well now it has a value and we made progress, so
bindings[s.id] = value
inc pc
else:
break
of fork:
let diff = code[pc].dest
# we follow pc + 1 and remember the label for later:
w.add pc+diff
inc pc
of goto: of goto:
let diff = code[pc].dest # we must leave endless loops eventually:
pc = pc + diff if not takenGotos.containsOrIncl(pc) or someChange:
if pc >= code.len: break pc = pc + code[pc].dest
else:
inc pc
of fork:
# we follow the next instruction but push the dest onto our "work" stack:
#if someChange:
w.add pc + code[pc].dest
inc pc
of use, useWithinCall:
#if not d[prevPc].missingOrExcl():
# someChange = true
consuming = code[pc].sym.id
inc pc
of def:
if not d[pc].containsOrIncl(code[pc].sym.id):
someChange = true
inc pc
when defined(useDfa) and defined(debugDfa):
for i in 0..<code.len:
echo "PC ", i, ": defs: ", d[i], "; uses ", u[i], "; consumes ", c[i]
# now check the condition we're interested in:
for i in 0..<code.len:
case code[i].kind
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)
if s.id in c[i]:
localError(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) =
var c = Con(code: @[], blocks: @[]) var c = Con(code: @[], blocks: @[])
gen(c, body) gen(c, body)
#echoCfg(c.code) when defined(useDfa) and defined(debugDfa): echoCfg(c.code)
dfa(c.code) dfa(c.code)
proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph = proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph =

View file

@ -86,10 +86,10 @@ proc mapType(t: ast.PType): ptr libffi.TType =
else: result = nil else: result = nil
of tyFloat, tyFloat64: result = addr libffi.type_double of tyFloat, tyFloat64: result = addr libffi.type_double
of tyFloat32: result = addr libffi.type_float 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: tyStmt, tyTypeDesc, tyProc, tyArray, tyStatic, tyNil:
result = addr libffi.type_pointer result = addr libffi.type_pointer
of tyDistinct, tyAlias: of tyDistinct, tyAlias, tySink:
result = mapType(t.sons[0]) result = mapType(t.sons[0])
else: else:
result = nil result = nil
@ -112,12 +112,12 @@ template `+!`(x, y: untyped): untyped =
proc packSize(v: PNode, typ: PType): int = proc packSize(v: PNode, typ: PType): int =
## computes the size of the blob ## computes the size of the blob
case typ.kind case typ.kind
of tyPtr, tyRef, tyVar: of tyPtr, tyRef, tyVar, tyLent:
if v.kind in {nkNilLit, nkPtrLit}: if v.kind in {nkNilLit, nkPtrLit}:
result = sizeof(pointer) result = sizeof(pointer)
else: else:
result = sizeof(pointer) + packSize(v.sons[0], typ.lastSon) result = sizeof(pointer) + packSize(v.sons[0], typ.lastSon)
of tyDistinct, tyGenericInst, tyAlias: of tyDistinct, tyGenericInst, tyAlias, tySink:
result = packSize(v, typ.sons[0]) result = packSize(v, typ.sons[0])
of tyArray: of tyArray:
# consider: ptr array[0..1000_000, int] which is common for interfacing; # consider: ptr array[0..1000_000, int] which is common for interfacing;
@ -209,7 +209,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
awr(cstring, cstring(v.strVal)) awr(cstring, cstring(v.strVal))
else: else:
globalError(v.info, "cannot map pointer/proc value to FFI") globalError(v.info, "cannot map pointer/proc value to FFI")
of tyPtr, tyRef, tyVar: of tyPtr, tyRef, tyVar, tyLent:
if v.kind == nkNilLit: if v.kind == nkNilLit:
# nothing to do since the memory is 0 initialized anyway # nothing to do since the memory is 0 initialized anyway
discard discard
@ -231,7 +231,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
packObject(v, typ, res) packObject(v, typ, res)
of tyNil: of tyNil:
discard discard
of tyDistinct, tyGenericInst, tyAlias: of tyDistinct, tyGenericInst, tyAlias, tySink:
pack(v, typ.sons[0], res) pack(v, typ.sons[0], res)
else: else:
globalError(v.info, "cannot map value to FFI " & typeToString(v.typ)) globalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
@ -364,7 +364,7 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
result = n result = n
else: else:
awi(nkPtrLit, cast[ByteAddress](p)) awi(nkPtrLit, cast[ByteAddress](p))
of tyPtr, tyRef, tyVar: of tyPtr, tyRef, tyVar, tyLent:
let p = rd(pointer, x) let p = rd(pointer, x)
if p.isNil: if p.isNil:
setNil() setNil()
@ -388,14 +388,14 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
aws(nkStrLit, $p) aws(nkStrLit, $p)
of tyNil: of tyNil:
setNil() setNil()
of tyDistinct, tyGenericInst, tyAlias: of tyDistinct, tyGenericInst, tyAlias, tySink:
result = unpack(x, typ.lastSon, n) result = unpack(x, typ.lastSon, n)
else: else:
# XXX what to do with 'array' here? # XXX what to do with 'array' here?
globalError(n.info, "cannot map value from FFI " & typeToString(typ)) globalError(n.info, "cannot map value from FFI " & typeToString(typ))
proc fficast*(x: PNode, destTyp: PType): PNode = 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, tyProc, tyCString, tyString,
tySequence}: tySequence}:
result = newNodeIT(x.kind, x.info, destTyp) result = newNodeIT(x.kind, x.info, destTyp)

View file

@ -109,7 +109,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
var evalTemplateCounter* = 0 var evalTemplateCounter* = 0
# to prevent endless recursion in templates instantiation # to prevent endless recursion in templates instantiation
proc wrapInComesFrom*(info: TLineInfo; res: PNode): PNode = proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
when true: when true:
result = res result = res
result.info = info result.info = info
@ -124,8 +124,12 @@ proc wrapInComesFrom*(info: TLineInfo; res: PNode): PNode =
if x[i].kind in nkCallKinds: if x[i].kind in nkCallKinds:
x.sons[i].info = info x.sons[i].info = info
else: else:
result = newNodeI(nkPar, info) result = newNodeI(nkStmtListExpr, info)
var d = newNodeI(nkComesFrom, info)
d.add newSymNode(sym, info)
result.add d
result.add res result.add res
result.typ = res.typ
proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode = proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode =
inc(evalTemplateCounter) inc(evalTemplateCounter)
@ -156,6 +160,6 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode =
for i in countup(0, safeLen(body) - 1): for i in countup(0, safeLen(body) - 1):
evalTemplateAux(body.sons[i], args, ctx, result) evalTemplateAux(body.sons[i], args, ctx, result)
result.flags.incl nfFromTemplate result.flags.incl nfFromTemplate
result = wrapInComesFrom(n.info, result) result = wrapInComesFrom(n.info, tmpl, result)
dec(evalTemplateCounter) dec(evalTemplateCounter)

View file

@ -21,7 +21,7 @@ import
type type
TSystemCC* = enum TSystemCC* = enum
ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc, ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
ccTcc, ccPcc, ccUcc, ccIcl ccTcc, ccPcc, ccUcc, ccIcl, ccIcc
TInfoCCProp* = enum # properties of the C compiler: TInfoCCProp* = enum # properties of the C compiler:
hasSwitchRange, # CC allows ranges in switch statements (GNU C) hasSwitchRange, # CC allows ranges in switch statements (GNU C)
hasComputedGoto, # CC has computed goto (GNU C extension) hasComputedGoto, # CC has computed goto (GNU C extension)
@ -135,16 +135,18 @@ compiler vcc:
# Intel C/C++ Compiler # Intel C/C++ Compiler
compiler icl: compiler icl:
# Intel compilers try to imitate the native ones (gcc and msvc) result = vcc()
when defined(windows):
result = vcc()
else:
result = gcc()
result.name = "icl" result.name = "icl"
result.compilerExe = "icl" result.compilerExe = "icl"
result.linkerExe = "icl" result.linkerExe = "icl"
# Intel compilers try to imitate the native ones (gcc and msvc)
compiler icc:
result = gcc()
result.name = "icc"
result.compilerExe = "icc"
result.linkerExe = "icc"
# Local C Compiler # Local C Compiler
compiler lcc: compiler lcc:
result = ( result = (
@ -251,7 +253,7 @@ compiler tcc:
compilerExe: "tcc", compilerExe: "tcc",
cppCompiler: "", cppCompiler: "",
compileTmpl: "-c $options $include -o $objfile $file", compileTmpl: "-c $options $include -o $objfile $file",
buildGui: "UNAVAILABLE!", buildGui: "-Wl,-subsystem=gui",
buildDll: " -shared", buildDll: " -shared",
buildLib: "", # XXX: not supported yet buildLib: "", # XXX: not supported yet
linkerExe: "tcc", linkerExe: "tcc",
@ -327,7 +329,8 @@ const
tcc(), tcc(),
pcc(), pcc(),
ucc(), ucc(),
icl()] icl(),
icc()]
hExt* = ".h" hExt* = ".h"
@ -791,42 +794,40 @@ proc writeJsonBuildInstructions*(projectfile: string) =
else: else:
f.write escapeJson(x) f.write escapeJson(x)
proc cfiles(f: File; buf: var string; list: CfileList, isExternal: bool) = proc cfiles(f: File; buf: var string; clist: CfileList, isExternal: bool) =
var i = 0 var pastStart = false
for it in list: for it in clist:
if CfileFlag.Cached in it.flags: continue if CfileFlag.Cached in it.flags: continue
let compileCmd = getCompileCFileCmd(it) let compileCmd = getCompileCFileCmd(it)
if pastStart: lit "],\L"
lit "[" lit "["
str it.cname str it.cname
lit ", " lit ", "
str compileCmd str compileCmd
inc i pastStart = true
if i == list.len: lit "]\L"
lit "]\L"
else:
lit "],\L"
proc linkfiles(f: File; buf, objfiles: var string) = proc linkfiles(f: File; buf, objfiles: var string; clist: CfileList;
for i, it in externalToLink: llist: seq[string]) =
let var pastStart = false
objFile = if noAbsolutePaths(): it.extractFilename else: it for it in llist:
objStr = addFileExt(objFile, CC[cCompiler].objExt) let objfile = if noAbsolutePaths(): it.extractFilename
else: it
let objstr = addFileExt(objfile, CC[cCompiler].objExt)
add(objfiles, ' ') add(objfiles, ' ')
add(objfiles, objStr) add(objfiles, objstr)
str objStr if pastStart: lit ",\L"
if toCompile.len == 0 and i == externalToLink.high: str objstr
lit "\L" pastStart = true
else:
lit ",\L" for it in clist:
for i, x in toCompile: let objstr = quoteShell(it.obj)
let objStr = quoteShell(x.obj)
add(objfiles, ' ') add(objfiles, ' ')
add(objfiles, objStr) add(objfiles, objstr)
str objStr if pastStart: lit ",\L"
if i == toCompile.high: str objstr
lit "\L" pastStart = true
else: lit "\L"
lit ",\L"
var buf = newStringOfCap(50) var buf = newStringOfCap(50)
@ -840,7 +841,7 @@ proc writeJsonBuildInstructions*(projectfile: string) =
lit "],\L\"link\":[\L" lit "],\L\"link\":[\L"
var objfiles = "" var objfiles = ""
# XXX add every file here that is to link # XXX add every file here that is to link
linkfiles(f, buf, objfiles) linkfiles(f, buf, objfiles, toCompile, externalToLink)
lit "],\L\"linkcmd\": " lit "],\L\"linkcmd\": "
str getLinkCmd(projectfile, objfiles) str getLinkCmd(projectfile, objfiles)

View file

@ -27,7 +27,7 @@ proc rawImportSymbol(c: PContext, s: PSym) =
# check if we have already a symbol of the same name: # check if we have already a symbol of the same name:
var check = strTableGet(c.importTable.symbols, s.name) var check = strTableGet(c.importTable.symbols, s.name)
if check != nil and check.id != s.id: if check != nil and check.id != s.id:
if s.kind notin OverloadableSyms: if s.kind notin OverloadableSyms or check.kind notin OverloadableSyms:
# s and check need to be qualified: # s and check need to be qualified:
incl(c.ambiguousSymbols, s.id) incl(c.ambiguousSymbols, s.id)
incl(c.ambiguousSymbols, check.id) incl(c.ambiguousSymbols, check.id)

View file

@ -173,7 +173,7 @@ const
proc mapType(typ: PType): TJSTypeKind = proc mapType(typ: PType): TJSTypeKind =
let t = skipTypes(typ, abstractInst) let t = skipTypes(typ, abstractInst)
case t.kind case t.kind
of tyVar, tyRef, tyPtr: of tyVar, tyRef, tyPtr, tyLent:
if skipTypes(t.lastSon, abstractInst).kind in MappedToObject: if skipTypes(t.lastSon, abstractInst).kind in MappedToObject:
result = etyObject result = etyObject
else: else:
@ -196,14 +196,15 @@ proc mapType(typ: PType): TJSTypeKind =
tyExpr, tyStmt, tyTypeDesc, tyBuiltInTypeClass, tyCompositeTypeClass, tyExpr, tyStmt, tyTypeDesc, tyBuiltInTypeClass, tyCompositeTypeClass,
tyAnd, tyOr, tyNot, tyAnything, tyVoid: tyAnd, tyOr, tyNot, tyAnything, tyVoid:
result = etyNone result = etyNone
of tyGenericInst, tyInferred, tyAlias, tyUserTypeClass, tyUserTypeClassInst: of tyGenericInst, tyInferred, tyAlias, tyUserTypeClass, tyUserTypeClassInst,
tySink:
result = mapType(typ.lastSon) result = mapType(typ.lastSon)
of tyStatic: of tyStatic:
if t.n != nil: result = mapType(lastSon t) if t.n != nil: result = mapType(lastSon t)
else: result = etyNone else: result = etyNone
of tyProc: result = etyProc of tyProc: result = etyProc
of tyCString: result = etyString of tyCString: result = etyString
of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("mapType") of tyUnused, tyOptAsRef: internalError("mapType")
proc mapType(p: PProc; typ: PType): TJSTypeKind = proc mapType(p: PProc; typ: PType): TJSTypeKind =
if p.target == targetPHP: result = etyObject if p.target == targetPHP: result = etyObject
@ -869,8 +870,8 @@ proc generateHeader(p: PProc, typ: PType): Rope =
add(result, name) add(result, name)
add(result, "_Idx") add(result, "_Idx")
elif not (i == 1 and param.name.s == "this"): elif not (i == 1 and param.name.s == "this"):
let k = param.typ.skipTypes({tyGenericInst, tyAlias}).kind let k = param.typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind
if k in {tyVar, tyRef, tyPtr, tyPointer}: if k in {tyVar, tyRef, tyPtr, tyLent, tyPointer}:
add(result, "&") add(result, "&")
add(result, "$") add(result, "$")
add(result, name) add(result, name)
@ -899,7 +900,7 @@ const
proc needsNoCopy(p: PProc; y: PNode): bool = proc needsNoCopy(p: PProc; y: PNode): bool =
result = (y.kind in nodeKindsNeedNoCopy) or result = (y.kind in nodeKindsNeedNoCopy) or
(skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyVar}) or (skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyLent, tyVar}) or
p.target == targetPHP p.target == targetPHP
proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
@ -1077,7 +1078,7 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) =
var ty = skipTypes(n.sons[0].typ, abstractVarRange) var ty = skipTypes(n.sons[0].typ, abstractVarRange)
if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) if ty.kind in {tyRef, tyPtr, tyLent}: ty = skipTypes(ty.lastSon, abstractVarRange)
case ty.kind case ty.kind
of tyArray, tyOpenArray, tySequence, tyString, tyCString, tyVarargs: of tyArray, tyOpenArray, tySequence, tyString, tyCString, tyVarargs:
genArrayAddr(p, n, r) genArrayAddr(p, n, r)
@ -1300,7 +1301,7 @@ proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes; emitted: ptr int =
add(r.res, ", ") add(r.res, ", ")
add(r.res, a.res) add(r.res, a.res)
if emitted != nil: inc emitted[] if emitted != nil: inc emitted[]
elif n.typ.kind == tyVar and n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex: elif n.typ.kind in {tyVar, tyLent} and n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex:
# this fixes bug #5608: # this fixes bug #5608:
let tmp = getTemp(p) let tmp = getTemp(p)
add(r.res, "($1 = $2, $1[0]), $1[1]" % [tmp, a.rdLoc]) add(r.res, "($1 = $2, $1[0]), $1[1]" % [tmp, a.rdLoc])
@ -1499,7 +1500,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
result = putToSeq("0", indirect) result = putToSeq("0", indirect)
of tyFloat..tyFloat128: of tyFloat..tyFloat128:
result = putToSeq("0.0", indirect) result = putToSeq("0.0", indirect)
of tyRange, tyGenericInst, tyAlias: of tyRange, tyGenericInst, tyAlias, tySink:
result = createVar(p, lastSon(typ), indirect) result = createVar(p, lastSon(typ), indirect)
of tySet: of tySet:
result = putToSeq("{}" | "array()", indirect) result = putToSeq("{}" | "array()", indirect)
@ -1546,7 +1547,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
createObjInitList(p, t, initIntSet(), initList) createObjInitList(p, t, initIntSet(), initList)
result = ("{$1}" | "array($#)") % [initList] result = ("{$1}" | "array($#)") % [initList]
if indirect: result = "[$1]" % [result] if indirect: result = "[$1]" % [result]
of tyVar, tyPtr, tyRef: of tyVar, tyPtr, tyLent, tyRef:
if mapType(p, t) == etyBaseIndex: if mapType(p, t) == etyBaseIndex:
result = putToSeq("[null, 0]", indirect) result = putToSeq("[null, 0]", indirect)
else: else:
@ -1579,7 +1580,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
let mname = mangleName(v, p.target) let mname = mangleName(v, p.target)
lineF(p, varCode & " = $3;$n" | "$$$2 = $3;$n", lineF(p, varCode & " = $3;$n" | "$$$2 = $3;$n",
[returnType, mname, createVar(p, v.typ, isIndirect(v))]) [returnType, mname, createVar(p, v.typ, isIndirect(v))])
if v.typ.kind in { tyVar, tyPtr, tyRef } and mapType(p, v.typ) == etyBaseIndex: if v.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and mapType(p, v.typ) == etyBaseIndex:
lineF(p, "var $1_Idx = 0;$n", [ mname ]) lineF(p, "var $1_Idx = 0;$n", [ mname ])
else: else:
discard mangleName(v, p.target) discard mangleName(v, p.target)
@ -1774,7 +1775,7 @@ proc genRepr(p: PProc, n: PNode, r: var TCompRes) =
proc genOf(p: PProc, n: PNode, r: var TCompRes) = proc genOf(p: PProc, n: PNode, r: var TCompRes) =
var x: TCompRes var x: TCompRes
let t = skipTypes(n.sons[2].typ, abstractVarRange+{tyRef, tyPtr, tyTypeDesc}) let t = skipTypes(n.sons[2].typ, abstractVarRange+{tyRef, tyPtr, tyLent, tyTypeDesc})
gen(p, n.sons[1], x) gen(p, n.sons[1], x)
if tfFinal in t.flags: if tfFinal in t.flags:
r.res = "($1.m_type == $2)" % [x.res, genTypeInfo(p, t)] r.res = "($1.m_type == $2)" % [x.res, genTypeInfo(p, t)]
@ -2051,10 +2052,10 @@ proc genConv(p: PProc, n: PNode, r: var TCompRes) =
return return
case dest.kind: case dest.kind:
of tyBool: of tyBool:
r.res = "(($1)? 1:0)" % [r.res] r.res = "(!!($1))" % [r.res]
r.kind = resExpr r.kind = resExpr
of tyInt: of tyInt:
r.res = "($1|0)" % [r.res] r.res = "(($1)|0)" % [r.res]
else: else:
# TODO: What types must we handle here? # TODO: What types must we handle here?
discard discard
@ -2161,7 +2162,8 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
let mname = mangleName(resultSym, p.target) let mname = mangleName(resultSym, p.target)
let resVar = createVar(p, resultSym.typ, isIndirect(resultSym)) let resVar = createVar(p, resultSym.typ, isIndirect(resultSym))
resultAsgn = p.indentLine(("var $# = $#;$n" | "$$$# = $#;$n") % [mname, resVar]) resultAsgn = p.indentLine(("var $# = $#;$n" | "$$$# = $#;$n") % [mname, resVar])
if resultSym.typ.kind in { tyVar, tyPtr, tyRef } and mapType(p, resultSym.typ) == etyBaseIndex: if resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and
mapType(p, resultSym.typ) == etyBaseIndex:
resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname]) resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname])
gen(p, prc.ast.sons[resultPos], a) gen(p, prc.ast.sons[resultPos], a)
if mapType(p, resultSym.typ) == etyBaseIndex: if mapType(p, resultSym.typ) == etyBaseIndex:
@ -2218,10 +2220,10 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) =
if dest.kind == src.kind: if dest.kind == src.kind:
# no-op conversion # no-op conversion
return return
let toInt = (dest.kind in tyInt .. tyInt32) let toInt = (dest.kind in tyInt..tyInt32)
let toUint = (dest.kind in tyUInt .. tyUInt32) let toUint = (dest.kind in tyUInt..tyUInt32)
let fromInt = (src.kind in tyInt .. tyInt32) let fromInt = (src.kind in tyInt..tyInt32)
let fromUint = (src.kind in tyUInt .. tyUInt32) let fromUint = (src.kind in tyUInt..tyUInt32)
if toUint and (fromInt or fromUint): if toUint and (fromInt or fromUint):
let trimmer = unsignedTrimmer(dest.size) let trimmer = unsignedTrimmer(dest.size)
@ -2252,7 +2254,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
case n.kind case n.kind
of nkSym: of nkSym:
genSym(p, n, r) genSym(p, n, r)
of nkCharLit..nkUInt32Lit: of nkCharLit..nkUInt64Lit:
if n.typ.kind == tyBool: if n.typ.kind == tyBool:
r.res = if n.intVal == 0: rope"false" else: rope"true" r.res = if n.intVal == 0: rope"false" else: rope"true"
else: else:
@ -2369,6 +2371,8 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
of nkGotoState, nkState: of nkGotoState, nkState:
internalError(n.info, "first class iterators not implemented") internalError(n.info, "first class iterators not implemented")
of nkPragmaBlock: gen(p, n.lastSon, r) of nkPragmaBlock: gen(p, n.lastSon, r)
of nkComesFrom:
discard "XXX to implement for better stack traces"
else: internalError(n.info, "gen: unknown node type: " & $n.kind) else: internalError(n.info, "gen: unknown node type: " & $n.kind)
var globals: PGlobals var globals: PGlobals

View file

@ -122,7 +122,7 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
[name, genTypeInfo(p, typ.sons[0])]) [name, genTypeInfo(p, typ.sons[0])])
proc genEnumInfoPHP(p: PProc; t: PType): Rope = proc genEnumInfoPHP(p: PProc; t: PType): Rope =
let t = t.skipTypes({tyGenericInst, tyDistinct, tyAlias}) let t = t.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink})
result = "$$NTI$1" % [rope(t.id)] result = "$$NTI$1" % [rope(t.id)]
p.declareGlobal(t.id, result) p.declareGlobal(t.id, result)
if containsOrIncl(p.g.typeInfoGenerated, t.id): return if containsOrIncl(p.g.typeInfoGenerated, t.id): return
@ -141,7 +141,7 @@ proc genEnumInfoPHP(p: PProc; t: PType): Rope =
proc genTypeInfo(p: PProc, typ: PType): Rope = proc genTypeInfo(p: PProc, typ: PType): Rope =
if p.target == targetPHP: if p.target == targetPHP:
return makeJSString(typeToString(typ, preferModuleInfo)) 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)] result = "NTI$1" % [rope(t.id)]
if containsOrIncl(p.g.typeInfoGenerated, t.id): return if containsOrIncl(p.g.typeInfoGenerated, t.id): return
case t.kind case t.kind
@ -152,7 +152,7 @@ proc genTypeInfo(p: PProc, typ: PType): Rope =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" % "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
[result, rope(ord(t.kind))] [result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s) prepend(p.g.typeInfo, s)
of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet: of tyVar, tyLent, tyRef, tyPtr, tySequence, tyRange, tySet:
var s = var s =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" % "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
[result, rope(ord(t.kind))] [result, rope(ord(t.kind))]

View file

@ -190,7 +190,7 @@ proc interestingVar(s: PSym): bool {.inline.} =
proc illegalCapture(s: PSym): bool {.inline.} = proc illegalCapture(s: PSym): bool {.inline.} =
result = skipTypes(s.typ, abstractInst).kind in result = skipTypes(s.typ, abstractInst).kind in
{tyVar, tyOpenArray, tyVarargs} or {tyVar, tyOpenArray, tyVarargs, tyLent} or
s.kind == skResult s.kind == skResult
proc isInnerProc(s: PSym): bool = proc isInnerProc(s: PSym): bool =
@ -455,6 +455,7 @@ type
LiftingPass = object LiftingPass = object
processed: IntSet processed: IntSet
envVars: Table[int, PNode] envVars: Table[int, PNode]
inContainer: int
proc initLiftingPass(fn: PSym): LiftingPass = proc initLiftingPass(fn: PSym): LiftingPass =
result.processed = initIntSet() result.processed = initIntSet()
@ -597,6 +598,8 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
proc transformYield(n: PNode; owner: PSym; d: DetectionPass; proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
c: var LiftingPass): PNode = c: var LiftingPass): PNode =
if c.inContainer > 0:
localError(n.info, "invalid control flow: 'yield' within a constructor")
let state = getStateField(owner) let state = getStateField(owner)
assert state != nil assert state != nil
assert state.typ != nil assert state.typ != nil
@ -703,11 +706,14 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
if not c.processed.containsOrIncl(s.id): if not c.processed.containsOrIncl(s.id):
#if s.name.s == "temp": #if s.name.s == "temp":
# echo renderTree(s.getBody, {renderIds}) # echo renderTree(s.getBody, {renderIds})
let oldInContainer = c.inContainer
c.inContainer = 0
let body = wrapIterBody(liftCapturedVars(s.getBody, s, d, c), s) let body = wrapIterBody(liftCapturedVars(s.getBody, s, d, c), s)
if c.envvars.getOrDefault(s.id).isNil: if c.envvars.getOrDefault(s.id).isNil:
s.ast.sons[bodyPos] = body s.ast.sons[bodyPos] = body
else: else:
s.ast.sons[bodyPos] = newTree(nkStmtList, rawClosureCreation(s, d, c), body) s.ast.sons[bodyPos] = newTree(nkStmtList, rawClosureCreation(s, d, c), body)
c.inContainer = oldInContainer
if s.typ.callConv == ccClosure: if s.typ.callConv == ccClosure:
result = symToClosure(n, owner, d, c) result = symToClosure(n, owner, d, c)
elif s.id in d.capturedVars: elif s.id in d.capturedVars:
@ -717,7 +723,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
result = accessViaEnvParam(n, owner) result = accessViaEnvParam(n, owner)
else: else:
result = accessViaEnvVar(n, owner, d, c) result = accessViaEnvVar(n, owner, d, c)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom,
nkTemplateDef, nkTypeSection: nkTemplateDef, nkTypeSection:
discard discard
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef: of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef:
@ -733,9 +739,12 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
n.sons[1] = x.sons[1] n.sons[1] = x.sons[1]
of nkLambdaKinds, nkIteratorDef, nkFuncDef: of nkLambdaKinds, nkIteratorDef, nkFuncDef:
if n.typ != nil and n[namePos].kind == nkSym: if n.typ != nil and n[namePos].kind == nkSym:
let oldInContainer = c.inContainer
c.inContainer = 0
let m = newSymNode(n[namePos].sym) let m = newSymNode(n[namePos].sym)
m.typ = n.typ m.typ = n.typ
result = liftCapturedVars(m, owner, d, c) result = liftCapturedVars(m, owner, d, c)
c.inContainer = oldInContainer
of nkHiddenStdConv: of nkHiddenStdConv:
if n.len == 2: if n.len == 2:
n.sons[1] = liftCapturedVars(n[1], owner, d, c) n.sons[1] = liftCapturedVars(n[1], owner, d, c)
@ -750,8 +759,12 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
# special case 'when nimVm' due to bug #3636: # special case 'when nimVm' due to bug #3636:
n.sons[1] = liftCapturedVars(n[1], owner, d, c) n.sons[1] = liftCapturedVars(n[1], owner, d, c)
return return
let inContainer = n.kind in {nkObjConstr, nkBracket}
if inContainer: inc c.inContainer
for i in 0..<n.len: for i in 0..<n.len:
n.sons[i] = liftCapturedVars(n[i], owner, d, c) n.sons[i] = liftCapturedVars(n[i], owner, d, c)
if inContainer: dec c.inContainer
# ------------------ old stuff ------------------------------------------- # ------------------ old stuff -------------------------------------------

View file

@ -445,13 +445,14 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
if result != nil and result.kind == skStub: loadStub(result) if result != nil and result.kind == skStub: loadStub(result)
proc pickSym*(c: PContext, n: PNode; kind: TSymKind; proc pickSym*(c: PContext, n: PNode; kinds: set[TSymKind];
flags: TSymFlags = {}): PSym = flags: TSymFlags = {}): PSym =
var o: TOverloadIter var o: TOverloadIter
var a = initOverloadIter(o, c, n) var a = initOverloadIter(o, c, n)
while a != nil: while a != nil:
if a.kind == kind and flags <= a.flags: if a.kind in kinds and flags <= a.flags:
return a if result == nil: result = a
else: return nil # ambiguous
a = nextOverloadIter(o, c, n) a = nextOverloadIter(o, c, n)
proc isInfixAs*(n: PNode): bool = proc isInfixAs*(n: PNode): bool =

View file

@ -330,7 +330,7 @@ proc typeNeedsNoDeepCopy(t: PType): bool =
# note that seq[T] is fine, but 'var seq[T]' is not, so we need to skip 'var' # 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 # for the stricter check and likewise we can skip 'seq' for a less
# strict check: # 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) result = not containsGarbageCollectedRef(t)
proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType; proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType;
@ -469,7 +469,7 @@ proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym,
# we pick n's type here, which hopefully is 'tyArray' and not # we pick n's type here, which hopefully is 'tyArray' and not
# 'tyOpenArray': # 'tyOpenArray':
var argType = n[i].typ.skipTypes(abstractInst) var argType = n[i].typ.skipTypes(abstractInst)
if i < formals.len and formals[i].typ.kind == tyVar: if i < formals.len and formals[i].typ.kind in {tyVar, tyLent}:
localError(n[i].info, "'spawn'ed function cannot have a 'var' parameter") localError(n[i].info, "'spawn'ed function cannot have a 'var' parameter")
#elif containsTyRef(argType): #elif containsTyRef(argType):
# localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure") # localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")

View file

@ -16,12 +16,12 @@ import
cgen, jsgen, json, nversion, cgen, jsgen, json, nversion,
platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen, platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
docgen2, service, parser, modules, ccgutils, sigmatch, ropes, docgen2, service, parser, modules, ccgutils, sigmatch, ropes,
modulegraphs modulegraphs, tables
from magicsys import systemModule, resetSysTypes from magicsys import systemModule, resetSysTypes
proc rodPass = proc rodPass =
if optSymbolFiles in gGlobalOptions: if gSymbolFiles in {enabledSf, writeOnlySf}:
registerPass(rodwritePass) registerPass(rodwritePass)
proc codegenPass = proc codegenPass =
@ -36,6 +36,9 @@ proc writeDepsFile(g: ModuleGraph; project: string) =
for m in g.modules: for m in g.modules:
if m != nil: if m != nil:
f.writeLine(toFullPath(m.position.int32)) f.writeLine(toFullPath(m.position.int32))
for k in g.inclToMod.keys:
if g.getModule(k).isNil: # don't repeat includes which are also modules
f.writeLine(k.toFullPath)
f.close() f.close()
proc commandGenDepend(graph: ModuleGraph; cache: IdentCache) = proc commandGenDepend(graph: ModuleGraph; cache: IdentCache) =
@ -77,6 +80,8 @@ proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) =
let proj = changeFileExt(gProjectFull, "") let proj = changeFileExt(gProjectFull, "")
extccomp.callCCompiler(proj) extccomp.callCCompiler(proj)
extccomp.writeJsonBuildInstructions(proj) extccomp.writeJsonBuildInstructions(proj)
if optGenScript in gGlobalOptions:
writeDepsFile(graph, toGeneratedFile(proj, ""))
proc commandJsonScript(graph: ModuleGraph; cache: IdentCache) = proc commandJsonScript(graph: ModuleGraph; cache: IdentCache) =
let proj = changeFileExt(gProjectFull, "") let proj = changeFileExt(gProjectFull, "")

View file

@ -26,7 +26,8 @@ type
errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation, errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation,
errExceptionExpected, errExceptionAlreadyHandled, errExceptionExpected, errExceptionAlreadyHandled,
errYieldNotAllowedHere, errYieldNotAllowedInTryStmt, errYieldNotAllowedHere, errYieldNotAllowedInTryStmt,
errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine, errInvalidNumberOfYieldExpr, errCannotReturnExpr,
errNoReturnWithReturnTypeNotAllowed, errAttemptToRedefine,
errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel, errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel,
errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected, errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected,
errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler, errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler,
@ -179,8 +180,9 @@ const
errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator", errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator",
errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expressions", errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expressions",
errCannotReturnExpr: "current routine cannot return an expression", errCannotReturnExpr: "current routine cannot return an expression",
errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type",
errAttemptToRedefine: "redefinition of \'$1\'", errAttemptToRedefine: "redefinition of \'$1\'",
errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\', \'raise\' or \'continue'", errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\', \'raise\', \'continue\' or proc call with noreturn pragma",
errStmtExpected: "statement expected", errStmtExpected: "statement expected",
errInvalidLabel: "\'$1\' is no label", errInvalidLabel: "\'$1\' is no label",
errInvalidCmdLineOption: "invalid command line option: \'$1\'", errInvalidCmdLineOption: "invalid command line option: \'$1\'",

View file

@ -28,6 +28,10 @@ proc newVersion*(ver: string): Version =
proc isSpecial(ver: Version): bool = proc isSpecial(ver: Version): bool =
return ($ver).len > 0 and ($ver)[0] == '#' return ($ver).len > 0 and ($ver)[0] == '#'
proc isValidVersion(v: string): bool =
if v.len > 0:
if v[0] in {'#'} + Digits: return true
proc `<`*(ver: Version, ver2: Version): bool = proc `<`*(ver: Version, ver2: Version): bool =
## This is synced from Nimble's version module. ## This is synced from Nimble's version module.
@ -72,15 +76,23 @@ proc getPathVersion*(p: string): tuple[name, version: string] =
result.name = p result.name = p
return return
for i in sepIdx..<p.len:
if p[i] in {DirSep, AltSep}:
result.name = p
return
result.name = p[0 .. sepIdx - 1] result.name = p[0 .. sepIdx - 1]
result.version = p.substr(sepIdx + 1) result.version = p.substr(sepIdx + 1)
proc addPackage(packages: StringTableRef, p: string) = proc addPackage(packages: StringTableRef, p: string; info: TLineInfo) =
let (name, ver) = getPathVersion(p) let (name, ver) = getPathVersion(p)
let version = newVersion(ver) if isValidVersion(ver):
if packages.getOrDefault(name).newVersion < version or let version = newVersion(ver)
(not packages.hasKey(name)): if packages.getOrDefault(name).newVersion < version or
packages[name] = $version (not packages.hasKey(name)):
packages[name] = $version
else:
localError(info, "invalid package name: " & p)
iterator chosen(packages: StringTableRef): string = iterator chosen(packages: StringTableRef): string =
for key, val in pairs(packages): for key, val in pairs(packages):
@ -109,7 +121,7 @@ proc addPathRec(dir: string, info: TLineInfo) =
if dir[pos] in {DirSep, AltSep}: inc(pos) if dir[pos] in {DirSep, AltSep}: inc(pos)
for k,p in os.walkDir(dir): for k,p in os.walkDir(dir):
if k == pcDir and p[pos] != '.': if k == pcDir and p[pos] != '.':
addPackage(packages, p) addPackage(packages, p, info)
for p in packages.chosen: for p in packages.chosen:
addNimblePath(p, info) addNimblePath(p, info)

View file

@ -48,7 +48,6 @@ type # please make sure we have under 32 options
optGenScript, # generate a script file to compile the *.c files optGenScript, # generate a script file to compile the *.c files
optGenMapping, # generate a mapping file optGenMapping, # generate a mapping file
optRun, # run the compiled project optRun, # run the compiled project
optSymbolFiles, # use symbol files for speeding up compilation
optCaasEnabled # compiler-as-a-service is running optCaasEnabled # compiler-as-a-service is running
optSkipConfigFile, # skip the general config file optSkipConfigFile, # skip the general config file
optSkipProjConfigFile, # skip the project's config file optSkipProjConfigFile, # skip the project's config file
@ -147,12 +146,19 @@ var
newDestructors*: bool newDestructors*: bool
gDynlibOverrideAll*: bool gDynlibOverrideAll*: bool
type
SymbolFilesOption* = enum
disabledSf, enabledSf, writeOnlySf, readOnlySf, v2Sf
var gSymbolFiles*: SymbolFilesOption
proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools} proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc
template preciseStack*(): bool = gPreciseStack template preciseStack*(): bool = gPreciseStack
template compilationCachePresent*: untyped = template compilationCachePresent*: untyped =
{optCaasEnabled, optSymbolFiles} * gGlobalOptions != {} gSymbolFiles in {enabledSf, writeOnlySf}
# {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {}
template optPreserveOrigSource*: untyped = template optPreserveOrigSource*: untyped =
optEmbedOrigSrc in gGlobalOptions optEmbedOrigSrc in gGlobalOptions

View file

@ -18,7 +18,7 @@ import
type type
TPassContext* = object of RootObj # the pass's context TPassContext* = object of RootObj # the pass's context
fromCache*: bool # true if created by "openCached" rd*: PRodReader # != nil if created by "openCached"
PPassContext* = ref TPassContext PPassContext* = ref TPassContext
@ -118,7 +118,7 @@ proc openPassesCached(g: ModuleGraph; a: var TPassContextArray, module: PSym,
if not isNil(gPasses[i].openCached): if not isNil(gPasses[i].openCached):
a[i] = gPasses[i].openCached(g, module, rd) a[i] = gPasses[i].openCached(g, module, rd)
if a[i] != nil: if a[i] != nil:
a[i].fromCache = true a[i].rd = rd
else: else:
a[i] = nil a[i] = nil

View file

@ -17,11 +17,12 @@ import
const const
FirstCallConv* = wNimcall FirstCallConv* = wNimcall
LastCallConv* = wNoconv LastCallConv* = wNoconv
nkPragmaCallKinds = {nkExprColonExpr, nkCall, nkCallStrLit}
const const
procPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, procPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wMagic, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader, wMagic, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge, wCompilerProc, wCore, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
wAsmNoStackFrame, wError, wDiscardable, wNoInit, wCodegenDecl, wAsmNoStackFrame, wError, wDiscardable, wNoInit, wCodegenDecl,
wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe, wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
@ -29,9 +30,9 @@ const
converterPragmas* = procPragmas converterPragmas* = procPragmas
methodPragmas* = procPragmas+{wBase}-{wImportCpp} methodPragmas* = procPragmas+{wBase}-{wImportCpp}
templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty, templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
wDelegator, wExportNims, wUsed} wDelegator, wExportNims, wUsed, wPragma}
macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc, macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
wNodecl, wMagic, wNosideeffect, wCompilerproc, wDeprecated, wExtern, wNodecl, wMagic, wNosideeffect, wCompilerProc, wCore, wDeprecated, wExtern,
wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator, wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator,
wExportNims, wUsed} wExportNims, wUsed}
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect, iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect,
@ -52,14 +53,14 @@ const
wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame, wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame,
wRaises, wLocks, wTags, wGcSafe} wRaises, wLocks, wTags, wGcSafe}
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wExtern, wShallow,
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef, wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked, wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
wBorrow, wGcSafe, wExportNims, wPartial, wUsed, wExplain, wPackage} wBorrow, wGcSafe, wExportNims, wPartial, wUsed, wExplain, wPackage}
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
wImportCpp, wImportObjC, wError, wGuard, wBitsize, wUsed} wImportCpp, wImportObjC, wError, wGuard, wBitsize, wUsed}
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern, wMagic, wHeader, wDeprecated, wCompilerProc, wCore, wDynlib, wExtern,
wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal, wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims, wUsed} wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims, wUsed}
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
@ -74,7 +75,7 @@ proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
let p = procAst[pragmasPos] let p = procAst[pragmasPos]
if p.kind == nkEmpty: return nil if p.kind == nkEmpty: return nil
for it in p: for it in p:
if it.kind == nkExprColonExpr and it[0].kind == nkIdent and if it.kind in nkPragmaCallKinds and it.len == 2 and it[0].kind == nkIdent and
it[0].ident.id == ord(name): it[0].ident.id == ord(name):
return it[1] return it[1]
@ -89,7 +90,7 @@ proc pragmaAsm*(c: PContext, n: PNode): char =
if n != nil: if n != nil:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
let it = n.sons[i] let it = n.sons[i]
if it.kind == nkExprColonExpr and it.sons[0].kind == nkIdent: if it.kind in nkPragmaCallKinds and it.len == 2 and it.sons[0].kind == nkIdent:
case whichKeyword(it.sons[0].ident) case whichKeyword(it.sons[0].ident)
of wSubsChar: of wSubsChar:
if it.sons[1].kind == nkCharLit: result = chr(int(it.sons[1].intVal)) if it.sons[1].kind == nkCharLit: result = chr(int(it.sons[1].intVal))
@ -151,7 +152,7 @@ proc newEmptyStrNode(n: PNode): PNode {.noinline.} =
result.strVal = "" result.strVal = ""
proc getStrLitNode(c: PContext, n: PNode): PNode = proc getStrLitNode(c: PContext, n: PNode): PNode =
if n.kind != nkExprColonExpr: if n.kind notin nkPragmaCallKinds or n.len != 2:
localError(n.info, errStringLiteralExpected) localError(n.info, errStringLiteralExpected)
# error correction: # error correction:
result = newEmptyStrNode(n) result = newEmptyStrNode(n)
@ -168,7 +169,7 @@ proc expectStrLit(c: PContext, n: PNode): string =
result = getStrLitNode(c, n).strVal result = getStrLitNode(c, n).strVal
proc expectIntLit(c: PContext, n: PNode): int = proc expectIntLit(c: PContext, n: PNode): int =
if n.kind != nkExprColonExpr: if n.kind notin nkPragmaCallKinds or n.len != 2:
localError(n.info, errIntLiteralExpected) localError(n.info, errIntLiteralExpected)
else: else:
n.sons[1] = c.semConstExpr(c, n.sons[1]) n.sons[1] = c.semConstExpr(c, n.sons[1])
@ -177,7 +178,7 @@ proc expectIntLit(c: PContext, n: PNode): int =
else: localError(n.info, errIntLiteralExpected) else: localError(n.info, errIntLiteralExpected)
proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string = proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string =
if n.kind == nkExprColonExpr: result = expectStrLit(c, n) if n.kind in nkPragmaCallKinds: result = expectStrLit(c, n)
else: result = defaultStr else: result = defaultStr
proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) = proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) =
@ -186,7 +187,7 @@ proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) =
proc processMagic(c: PContext, n: PNode, s: PSym) = proc processMagic(c: PContext, n: PNode, s: PSym) =
#if sfSystemModule notin c.module.flags: #if sfSystemModule notin c.module.flags:
# liMessage(n.info, errMagicOnlyInSystem) # liMessage(n.info, errMagicOnlyInSystem)
if n.kind != nkExprColonExpr: if n.kind notin nkPragmaCallKinds or n.len != 2:
localError(n.info, errStringLiteralExpected) localError(n.info, errStringLiteralExpected)
return return
var v: string var v: string
@ -204,7 +205,7 @@ proc wordToCallConv(sw: TSpecialWord): TCallingConvention =
result = TCallingConvention(ord(ccDefault) + ord(sw) - ord(wNimcall)) result = TCallingConvention(ord(ccDefault) + ord(sw) - ord(wNimcall))
proc isTurnedOn(c: PContext, n: PNode): bool = proc isTurnedOn(c: PContext, n: PNode): bool =
if n.kind == nkExprColonExpr: if n.kind in nkPragmaCallKinds and n.len == 2:
let x = c.semConstBoolExpr(c, n.sons[1]) let x = c.semConstBoolExpr(c, n.sons[1])
n.sons[1] = x n.sons[1] = x
if x.kind == nkIntLit: return x.intVal != 0 if x.kind == nkIntLit: return x.intVal != 0
@ -223,7 +224,7 @@ proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) =
else: excl(c.module.flags, flag) else: excl(c.module.flags, flag)
proc processCallConv(c: PContext, n: PNode) = proc processCallConv(c: PContext, n: PNode) =
if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent): if n.kind in nkPragmaCallKinds and n.len == 2 and n.sons[1].kind == nkIdent:
var sw = whichKeyword(n.sons[1].ident) var sw = whichKeyword(n.sons[1].ident)
case sw case sw
of FirstCallConv..LastCallConv: of FirstCallConv..LastCallConv:
@ -244,7 +245,7 @@ proc getLib(c: PContext, kind: TLibKind, path: PNode): PLib =
result.isOverriden = options.isDynlibOverride(path.strVal) result.isOverriden = options.isDynlibOverride(path.strVal)
proc expectDynlibNode(c: PContext, n: PNode): PNode = proc expectDynlibNode(c: PContext, n: PNode): PNode =
if n.kind != nkExprColonExpr: if n.kind notin nkPragmaCallKinds or n.len != 2:
localError(n.info, errStringLiteralExpected) localError(n.info, errStringLiteralExpected)
# error correction: # error correction:
result = newEmptyStrNode(n) result = newEmptyStrNode(n)
@ -264,7 +265,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) =
if not lib.isOverriden: if not lib.isOverriden:
c.optionStack[^1].dynlib = lib c.optionStack[^1].dynlib = lib
else: else:
if n.kind == nkExprColonExpr: if n.kind in nkPragmaCallKinds:
var lib = getLib(c, libDynamic, expectDynlibNode(c, n)) var lib = getLib(c, libDynamic, expectDynlibNode(c, n))
if not lib.isOverriden: if not lib.isOverriden:
addToLib(lib, sym) addToLib(lib, sym)
@ -279,7 +280,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) =
sym.typ.callConv = ccCDecl sym.typ.callConv = ccCDecl
proc processNote(c: PContext, n: PNode) = proc processNote(c: PContext, n: PNode) =
if (n.kind == nkExprColonExpr) and (sonsLen(n) == 2) and if (n.kind in nkPragmaCallKinds) and (sonsLen(n) == 2) and
(n.sons[0].kind == nkBracketExpr) and (n.sons[0].kind == nkBracketExpr) and
(n.sons[0].sons.len == 2) and (n.sons[0].sons.len == 2) and
(n.sons[0].sons[1].kind == nkIdent) and (n.sons[0].sons[1].kind == nkIdent) and
@ -307,7 +308,7 @@ proc processNote(c: PContext, n: PNode) =
invalidPragma(n) invalidPragma(n)
proc processOption(c: PContext, n: PNode): bool = proc processOption(c: PContext, n: PNode): bool =
if n.kind != nkExprColonExpr: result = true if n.kind notin nkPragmaCallKinds or n.len != 2: result = true
elif n.sons[0].kind == nkBracketExpr: processNote(c, n) elif n.sons[0].kind == nkBracketExpr: processNote(c, n)
elif n.sons[0].kind != nkIdent: result = true elif n.sons[0].kind != nkIdent: result = true
else: else:
@ -355,8 +356,8 @@ proc processOption(c: PContext, n: PNode): bool =
else: result = true else: result = true
proc processPush(c: PContext, n: PNode, start: int) = proc processPush(c: PContext, n: PNode, start: int) =
if n.sons[start-1].kind == nkExprColonExpr: if n.sons[start-1].kind in nkPragmaCallKinds:
localError(n.info, errGenerated, "':' after 'push' not supported") localError(n.info, errGenerated, "'push' can't have arguments")
var x = newOptionEntry() var x = newOptionEntry()
var y = c.optionStack[^1] var y = c.optionStack[^1]
x.options = gOptions x.options = gOptions
@ -381,14 +382,14 @@ proc processPop(c: PContext, n: PNode) =
c.optionStack.setLen(c.optionStack.len - 1) c.optionStack.setLen(c.optionStack.len - 1)
proc processDefine(c: PContext, n: PNode) = proc processDefine(c: PContext, n: PNode) =
if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent): if (n.kind in nkPragmaCallKinds and n.len == 2) and (n.sons[1].kind == nkIdent):
defineSymbol(n.sons[1].ident.s) defineSymbol(n.sons[1].ident.s)
message(n.info, warnDeprecated, "define") message(n.info, warnDeprecated, "define")
else: else:
invalidPragma(n) invalidPragma(n)
proc processUndef(c: PContext, n: PNode) = proc processUndef(c: PContext, n: PNode) =
if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent): if (n.kind in nkPragmaCallKinds and n.len == 2) and (n.sons[1].kind == nkIdent):
undefSymbol(n.sons[1].ident.s) undefSymbol(n.sons[1].ident.s)
message(n.info, warnDeprecated, "undef") message(n.info, warnDeprecated, "undef")
else: else:
@ -420,7 +421,7 @@ proc processCompile(c: PContext, n: PNode) =
localError(n.info, errStringLiteralExpected) localError(n.info, errStringLiteralExpected)
result = "" result = ""
let it = if n.kind == nkExprColonExpr: n.sons[1] else: n let it = if n.kind in nkPragmaCallKinds and n.len == 2: n.sons[1] else: n
if it.kind == nkPar and it.len == 2: if it.kind == nkPar and it.len == 2:
let s = getStrLit(c, it, 0) let s = getStrLit(c, it, 0)
let dest = getStrLit(c, it, 1) let dest = getStrLit(c, it, 1)
@ -453,7 +454,7 @@ proc pragmaBreakpoint(c: PContext, n: PNode) =
discard getOptionalStr(c, n, "") discard getOptionalStr(c, n, "")
proc pragmaWatchpoint(c: PContext, n: PNode) = proc pragmaWatchpoint(c: PContext, n: PNode) =
if n.kind == nkExprColonExpr: if n.kind in nkPragmaCallKinds and n.len == 2:
n.sons[1] = c.semExpr(c, n.sons[1]) n.sons[1] = c.semExpr(c, n.sons[1])
else: else:
invalidPragma(n) invalidPragma(n)
@ -494,7 +495,7 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
result = newNode(nkAsmStmt, n.info) result = newNode(nkAsmStmt, n.info)
proc pragmaEmit(c: PContext, n: PNode) = proc pragmaEmit(c: PContext, n: PNode) =
if n.kind != nkExprColonExpr: if n.kind notin nkPragmaCallKinds or n.len != 2:
localError(n.info, errStringLiteralExpected) localError(n.info, errStringLiteralExpected)
else: else:
let n1 = n[1] let n1 = n[1]
@ -512,12 +513,12 @@ proc pragmaEmit(c: PContext, n: PNode) =
localError(n.info, errStringLiteralExpected) localError(n.info, errStringLiteralExpected)
proc noVal(n: PNode) = proc noVal(n: PNode) =
if n.kind == nkExprColonExpr: invalidPragma(n) if n.kind in nkPragmaCallKinds and n.len > 1: invalidPragma(n)
proc pragmaUnroll(c: PContext, n: PNode) = proc pragmaUnroll(c: PContext, n: PNode) =
if c.p.nestedLoopCounter <= 0: if c.p.nestedLoopCounter <= 0:
invalidPragma(n) invalidPragma(n)
elif n.kind == nkExprColonExpr: elif n.kind in nkPragmaCallKinds and n.len == 2:
var unrollFactor = expectIntLit(c, n) var unrollFactor = expectIntLit(c, n)
if unrollFactor <% 32: if unrollFactor <% 32:
n.sons[1] = newIntNode(nkIntLit, unrollFactor) n.sons[1] = newIntNode(nkIntLit, unrollFactor)
@ -525,10 +526,11 @@ proc pragmaUnroll(c: PContext, n: PNode) =
invalidPragma(n) invalidPragma(n)
proc pragmaLine(c: PContext, n: PNode) = proc pragmaLine(c: PContext, n: PNode) =
if n.kind == nkExprColonExpr: if n.kind in nkPragmaCallKinds and n.len == 2:
n.sons[1] = c.semConstExpr(c, n.sons[1]) n.sons[1] = c.semConstExpr(c, n.sons[1])
let a = n.sons[1] let a = n.sons[1]
if a.kind == nkPar: if a.kind == nkPar:
# unpack the tuple
var x = a.sons[0] var x = a.sons[0]
var y = a.sons[1] var y = a.sons[1]
if x.kind == nkExprColonExpr: x = x.sons[1] if x.kind == nkExprColonExpr: x = x.sons[1]
@ -549,7 +551,7 @@ proc pragmaLine(c: PContext, n: PNode) =
proc processPragma(c: PContext, n: PNode, i: int) = proc processPragma(c: PContext, n: PNode, i: int) =
var it = n.sons[i] var it = n.sons[i]
if it.kind != nkExprColonExpr: invalidPragma(n) if it.kind notin nkPragmaCallKinds and it.len == 2: invalidPragma(n)
elif it.sons[0].kind != nkIdent: invalidPragma(n) elif it.sons[0].kind != nkIdent: invalidPragma(n)
elif it.sons[1].kind != nkIdent: invalidPragma(n) elif it.sons[1].kind != nkIdent: invalidPragma(n)
@ -566,7 +568,7 @@ proc pragmaRaisesOrTags(c: PContext, n: PNode) =
localError(x.info, errGenerated, "invalid type for raises/tags list") localError(x.info, errGenerated, "invalid type for raises/tags list")
x.typ = t x.typ = t
if n.kind == nkExprColonExpr: if n.kind in nkPragmaCallKinds and n.len == 2:
let it = n.sons[1] let it = n.sons[1]
if it.kind notin {nkCurly, nkBracket}: if it.kind notin {nkCurly, nkBracket}:
processExc(c, it) processExc(c, it)
@ -576,7 +578,7 @@ proc pragmaRaisesOrTags(c: PContext, n: PNode) =
invalidPragma(n) invalidPragma(n)
proc pragmaLockStmt(c: PContext; it: PNode) = proc pragmaLockStmt(c: PContext; it: PNode) =
if it.kind != nkExprColonExpr: if it.kind notin nkPragmaCallKinds or it.len != 2:
invalidPragma(it) invalidPragma(it)
else: else:
let n = it[1] let n = it[1]
@ -587,7 +589,7 @@ proc pragmaLockStmt(c: PContext; it: PNode) =
n.sons[i] = c.semExpr(c, n.sons[i]) n.sons[i] = c.semExpr(c, n.sons[i])
proc pragmaLocks(c: PContext, it: PNode): TLockLevel = proc pragmaLocks(c: PContext, it: PNode): TLockLevel =
if it.kind != nkExprColonExpr: if it.kind notin nkPragmaCallKinds or it.len != 2:
invalidPragma(it) invalidPragma(it)
else: else:
case it[1].kind case it[1].kind
@ -604,7 +606,7 @@ proc pragmaLocks(c: PContext, it: PNode): TLockLevel =
result = TLockLevel(x) result = TLockLevel(x)
proc typeBorrow(sym: PSym, n: PNode) = proc typeBorrow(sym: PSym, n: PNode) =
if n.kind == nkExprColonExpr: if n.kind in nkPragmaCallKinds and n.len == 2:
let it = n.sons[1] let it = n.sons[1]
if it.kind != nkAccQuoted: if it.kind != nkAccQuoted:
localError(n.info, "a type can only borrow `.` for now") localError(n.info, "a type can only borrow `.` for now")
@ -624,7 +626,7 @@ proc deprecatedStmt(c: PContext; pragma: PNode) =
if pragma.kind != nkBracket: if pragma.kind != nkBracket:
localError(pragma.info, "list of key:value pairs expected"); return localError(pragma.info, "list of key:value pairs expected"); return
for n in pragma: for n in pragma:
if n.kind in {nkExprColonExpr, nkExprEqExpr}: if n.kind in nkPragmaCallKinds and n.len == 2:
let dest = qualifiedLookUp(c, n[1], {checkUndeclared}) let dest = qualifiedLookUp(c, n[1], {checkUndeclared})
if dest == nil or dest.kind in routineKinds: if dest == nil or dest.kind in routineKinds:
localError(n.info, warnUser, "the .deprecated pragma is unreliable for routines") localError(n.info, warnUser, "the .deprecated pragma is unreliable for routines")
@ -638,7 +640,7 @@ proc deprecatedStmt(c: PContext; pragma: PNode) =
localError(n.info, "key:value pair expected") localError(n.info, "key:value pair expected")
proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym = proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
if it.kind != nkExprColonExpr: if it.kind notin nkPragmaCallKinds or it.len != 2:
invalidPragma(it); return invalidPragma(it); return
let n = it[1] let n = it[1]
if n.kind == nkSym: if n.kind == nkSym:
@ -655,13 +657,36 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
else: else:
result = qualifiedLookUp(c, n, {checkUndeclared}) result = qualifiedLookUp(c, n, {checkUndeclared})
proc semCustomPragma(c: PContext, n: PNode): PNode =
assert(n.kind in nkPragmaCallKinds + {nkIdent})
if n.kind == nkIdent:
result = newTree(nkCall, n)
elif n.kind == nkExprColonExpr:
# pragma: arg -> pragma(arg)
result = newTree(nkCall, n[0], n[1])
else:
result = n
result = c.semOverloadedCall(c, result, n, {skTemplate}, {})
if sfCustomPragma notin result[0].sym.flags:
invalidPragma(n)
if n.kind == nkIdent:
result = result[0]
elif n.kind == nkExprColonExpr:
result.kind = n.kind # pragma(arg) -> pragma: arg
proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
validPragmas: TSpecialWords): bool = validPragmas: TSpecialWords): bool =
var it = n.sons[i] var it = n.sons[i]
var key = if it.kind == nkExprColonExpr: it.sons[0] else: it var key = if it.kind in nkPragmaCallKinds and it.len > 1: it.sons[0] else: it
if key.kind == nkBracketExpr: if key.kind == nkBracketExpr:
processNote(c, it) processNote(c, it)
return return
elif key.kind notin nkIdentKinds:
n.sons[i] = semCustomPragma(c, it)
return
let ident = considerQuotedIdent(key) let ident = considerQuotedIdent(key)
var userPragma = strTableGet(c.userPragmas, ident) var userPragma = strTableGet(c.userPragmas, ident)
if userPragma != nil: if userPragma != nil:
@ -771,9 +796,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
of wNoreturn: of wNoreturn:
noVal(it) noVal(it)
incl(sym.flags, sfNoReturn) incl(sym.flags, sfNoReturn)
if sym.typ[0] != nil:
localError(sym.ast[paramsPos][0].info, errNoReturnWithReturnTypeNotAllowed)
of wDynlib: of wDynlib:
processDynLib(c, it, sym) processDynLib(c, it, sym)
of wCompilerproc: of wCompilerProc, wCore:
noVal(it) # compilerproc may not get a string! noVal(it) # compilerproc may not get a string!
cppDefine(c.graph.config, sym.name.s) cppDefine(c.graph.config, sym.name.s)
if sfFromGeneric notin sym.flags: markCompilerProc(sym) if sfFromGeneric notin sym.flags: markCompilerProc(sym)
@ -783,7 +810,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
of wExplain: of wExplain:
sym.flags.incl sfExplain sym.flags.incl sfExplain
of wDeprecated: of wDeprecated:
if it.kind == nkExprColonExpr: deprecatedStmt(c, it) if it.kind in nkPragmaCallKinds: deprecatedStmt(c, it)
elif sym != nil: incl(sym.flags, sfDeprecated) elif sym != nil: incl(sym.flags, sfDeprecated)
else: incl(c.module.flags, sfDeprecated) else: incl(c.module.flags, sfDeprecated)
of wVarargs: of wVarargs:
@ -862,8 +889,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
result = true result = true
of wPop: processPop(c, it) of wPop: processPop(c, it)
of wPragma: of wPragma:
processPragma(c, n, i) if not sym.isNil and sym.kind == skTemplate:
result = true sym.flags.incl sfCustomPragma
else:
processPragma(c, n, i)
result = true
of wDiscardable: of wDiscardable:
noVal(it) noVal(it)
if sym != nil: incl(sym.flags, sfDiscardable) if sym != nil: incl(sym.flags, sfDiscardable)
@ -937,7 +967,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
elif sym.typ == nil: invalidPragma(it) elif sym.typ == nil: invalidPragma(it)
else: sym.typ.lockLevel = pragmaLocks(c, it) else: sym.typ.lockLevel = pragmaLocks(c, it)
of wBitsize: of wBitsize:
if sym == nil or sym.kind != skField or it.kind != nkExprColonExpr: if sym == nil or sym.kind != skField:
invalidPragma(it) invalidPragma(it)
else: else:
sym.bitsize = expectIntLit(c, it) sym.bitsize = expectIntLit(c, it)
@ -955,7 +985,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
if sym == nil: invalidPragma(it) if sym == nil: invalidPragma(it)
else: magicsys.registerNimScriptSymbol(sym) else: magicsys.registerNimScriptSymbol(sym)
of wInjectStmt: of wInjectStmt:
if it.kind != nkExprColonExpr: if it.kind notin nkPragmaCallKinds or it.len != 2:
localError(it.info, errExprExpected) localError(it.info, errExprExpected)
else: else:
it.sons[1] = c.semExpr(c, it.sons[1]) it.sons[1] = c.semExpr(c, it.sons[1])
@ -966,10 +996,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
else: else:
localError(it.info, "'experimental' pragma only valid as toplevel statement") localError(it.info, "'experimental' pragma only valid as toplevel statement")
of wThis: of wThis:
if it.kind == nkExprColonExpr: if it.kind in nkPragmaCallKinds and it.len == 2:
c.selfName = considerQuotedIdent(it[1]) c.selfName = considerQuotedIdent(it[1])
else: elif it.kind == nkIdent or it.len == 1:
c.selfName = getIdent("self") c.selfName = getIdent("self")
else:
localError(it.info, "'this' pragma is allowed to have zero or one arguments")
of wNoRewrite: of wNoRewrite:
noVal(it) noVal(it)
of wBase: of wBase:
@ -985,7 +1017,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
else: sym.flags.incl sfUsed else: sym.flags.incl sfUsed
of wLiftLocals: discard of wLiftLocals: discard
else: invalidPragma(it) else: invalidPragma(it)
else: invalidPragma(it) else:
n.sons[i] = semCustomPragma(c, it)
proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
validPragmas: TSpecialWords) = validPragmas: TSpecialWords) =
@ -1013,7 +1047,7 @@ proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
return false return false
for p in n.sons: for p in n.sons:
var key = if p.kind == nkExprColonExpr: p[0] else: p var key = if p.kind in nkPragmaCallKinds and p.len > 1: p[0] else: p
if key.kind == nkIdent and whichKeyword(key.ident) == pragma: if key.kind == nkIdent and whichKeyword(key.ident) == pragma:
return true return true

View file

@ -175,8 +175,17 @@ proc put(g: var TSrcGen, kind: TTokType, s: string) =
proc toNimChar(c: char): string = proc toNimChar(c: char): string =
case c case c
of '\0': result = "\\0" of '\0': result = "\\x00" # not "\\0" to avoid ambiguous cases like "\\012".
of '\x01'..'\x1F', '\x80'..'\xFF': result = "\\x" & strutils.toHex(ord(c), 2) 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 of '\'', '\"', '\\': result = '\\' & c
else: result = c & "" else: result = c & ""
@ -316,8 +325,8 @@ proc lsub(g: TSrcGen; n: PNode): int
proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string = proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
proc skip(t: PType): PType = proc skip(t: PType): PType =
result = t result = t
while result.kind in {tyGenericInst, tyRange, tyVar, tyDistinct, while result.kind in {tyGenericInst, tyRange, tyVar, tyLent, tyDistinct,
tyOrdinal, tyAlias}: tyOrdinal, tyAlias, tySink}:
result = lastSon(result) result = lastSon(result)
if n.typ != nil and n.typ.skip.kind in {tyBool, tyEnum}: if n.typ != nil and n.typ.skip.kind in {tyBool, tyEnum}:
let enumfields = n.typ.skip.n let enumfields = n.typ.skip.n
@ -710,6 +719,7 @@ proc gcase(g: var TSrcGen, n: PNode) =
var c: TContext var c: TContext
initContext(c) initContext(c)
var length = sonsLen(n) var length = sonsLen(n)
if length == 0: return
var last = if n.sons[length-1].kind == nkElse: -2 else: -1 var last = if n.sons[length-1].kind == nkElse: -2 else: -1
if longMode(g, n, 0, last): incl(c.flags, rfLongMode) if longMode(g, n, 0, last): incl(c.flags, rfLongMode)
putWithSpace(g, tkCase, "case") putWithSpace(g, tkCase, "case")
@ -860,7 +870,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
a: TContext a: TContext
if n.comment != nil: pushCom(g, n) if n.comment != nil: pushCom(g, n)
case n.kind # atoms: case n.kind # atoms:
of nkTripleStrLit: putRawStr(g, tkTripleStrLit, n.strVal) of nkTripleStrLit: put(g, tkTripleStrLit, atom(g, n))
of nkEmpty: discard of nkEmpty: discard
of nkType: put(g, tkInvalid, atom(g, n)) of nkType: put(g, tkInvalid, atom(g, n))
of nkSym, nkIdent: gident(g, n) of nkSym, nkIdent: gident(g, n)
@ -888,6 +898,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkBracketLe, "[") put(g, tkBracketLe, "[")
gcomma(g, n, 2) gcomma(g, n, 2)
put(g, tkBracketRi, "]") 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: else:
if sonsLen(n) >= 1: gsub(g, n.sons[0]) if sonsLen(n) >= 1: gsub(g, n.sons[0])
put(g, tkParLe, "(") put(g, tkParLe, "(")
@ -1397,8 +1415,8 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkBracketRi, "]") put(g, tkBracketRi, "]")
of nkTupleClassTy: of nkTupleClassTy:
put(g, tkTuple, "tuple") put(g, tkTuple, "tuple")
of nkMetaNode_Obsolete: of nkComesFrom:
put(g, tkParLe, "(META|") put(g, tkParLe, "(ComesFrom|")
gsub(g, n, 0) gsub(g, n, 0)
put(g, tkParRi, ")") put(g, tkParRi, ")")
of nkGotoState, nkState: of nkGotoState, nkState:

View file

@ -861,12 +861,11 @@ proc loadMethods(r: PRodReader) =
if r.s[r.pos] == ' ': inc(r.pos) if r.s[r.pos] == ' ': inc(r.pos)
proc getHash*(fileIdx: int32): SecureHash = proc getHash*(fileIdx: int32): SecureHash =
internalAssert fileIdx >= 0 and fileIdx < gMods.len if fileIdx <% gMods.len and gMods[fileIdx].hashDone:
if gMods[fileIdx].hashDone:
return gMods[fileIdx].hash return gMods[fileIdx].hash
result = secureHashFile(fileIdx.toFullPath) result = secureHashFile(fileIdx.toFullPath)
if fileIdx >= gMods.len: setLen(gMods, fileIdx+1)
gMods[fileIdx].hash = result gMods[fileIdx].hash = result
template growCache*(cache, pos) = template growCache*(cache, pos) =
@ -912,7 +911,7 @@ proc checkDep(fileIdx: int32; cache: IdentCache): TReasonForRecompile =
proc handleSymbolFile*(module: PSym; cache: IdentCache): PRodReader = proc handleSymbolFile*(module: PSym; cache: IdentCache): PRodReader =
let fileIdx = module.fileIdx let fileIdx = module.fileIdx
if optSymbolFiles notin gGlobalOptions: if gSymbolFiles in {disabledSf, writeOnlySf}:
module.id = getID() module.id = getID()
return nil return nil
idgen.loadMaxIds(options.gProjectPath / options.gProjectName) idgen.loadMaxIds(options.gProjectPath / options.gProjectName)

View file

@ -73,7 +73,8 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
cbos copyFile: cbos copyFile:
os.copyFile(getString(a, 0), getString(a, 1)) os.copyFile(getString(a, 0), getString(a, 1))
cbos getLastModificationTime: cbos getLastModificationTime:
setResult(a, toSeconds(getLastModificationTime(getString(a, 0)))) # depends on Time's implementation!
setResult(a, int64(getLastModificationTime(getString(a, 0))))
cbos rawExec: cbos rawExec:
setResult(a, osproc.execCmd getString(a, 0)) setResult(a, osproc.execCmd getString(a, 0))

View file

@ -74,7 +74,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
localError(arg.info, errExprXHasNoType, localError(arg.info, errExprXHasNoType,
renderTree(arg, {renderNoComments})) renderTree(arg, {renderNoComments}))
# error correction: # error correction:
result = copyNode(arg) result = copyTree(arg)
result.typ = formal result.typ = formal
else: else:
result = indexTypesMatch(c, formal, arg.typ, arg) result = indexTypesMatch(c, formal, arg.typ, arg)
@ -102,8 +102,8 @@ proc commonType*(x, y: PType): PType =
# if expressions, etc.: # if expressions, etc.:
if x == nil: return x if x == nil: return x
if y == nil: return y if y == nil: return y
var a = skipTypes(x, {tyGenericInst, tyAlias}) var a = skipTypes(x, {tyGenericInst, tyAlias, tySink})
var b = skipTypes(y, {tyGenericInst, tyAlias}) var b = skipTypes(y, {tyGenericInst, tyAlias, tySink})
result = x result = x
if a.kind in {tyExpr, tyNil}: result = y if a.kind in {tyExpr, tyNil}: result = y
elif b.kind in {tyExpr, tyNil}: result = x elif b.kind in {tyExpr, tyNil}: result = x
@ -165,6 +165,19 @@ proc commonType*(x, y: PType): PType =
result = newType(k, r.owner) result = newType(k, r.owner)
result.addSonSkipIntLit(r) result.addSonSkipIntLit(r)
proc endsInNoReturn(n: PNode): bool =
# check if expr ends in raise exception or call of noreturn proc
var it = n
while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
it = it.lastSon
result = it.kind == nkRaiseStmt or
it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
proc commonType*(x: PType, y: PNode): PType =
# ignore exception raising branches in case/if expressions
if endsInNoReturn(y): return x
commonType(x, y.typ)
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info) result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info)
when defined(nimsuggest): when defined(nimsuggest):
@ -423,7 +436,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
result = evalMacroCall(c.module, c.cache, n, nOrig, sym) result = evalMacroCall(c.module, c.cache, n, nOrig, sym)
if efNoSemCheck notin flags: if efNoSemCheck notin flags:
result = semAfterMacroCall(c, n, result, sym, flags) result = semAfterMacroCall(c, n, result, sym, flags)
result = wrapInComesFrom(nOrig.info, result) result = wrapInComesFrom(nOrig.info, sym, result)
popInfoContext() popInfoContext()
proc forceBool(c: PContext, n: PNode): PNode = proc forceBool(c: PContext, n: PNode): PNode =
@ -488,6 +501,8 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
proc myOpenCached(graph: ModuleGraph; module: PSym; rd: PRodReader): PPassContext = proc myOpenCached(graph: ModuleGraph; module: PSym; rd: PRodReader): PPassContext =
result = myOpen(graph, module, rd.cache) result = myOpen(graph, module, rd.cache)
proc replayMethodDefs(graph: ModuleGraph; rd: PRodReader) =
for m in items(rd.methods): methodDef(graph, m, true) for m in items(rd.methods): methodDef(graph, m, true)
proc isImportSystemStmt(n: PNode): bool = proc isImportSystemStmt(n: PNode): bool =
@ -594,6 +609,8 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
addCodeForGenerics(c, result) addCodeForGenerics(c, result)
if c.module.ast != nil: if c.module.ast != nil:
result.add(c.module.ast) result.add(c.module.ast)
if c.rd != nil:
replayMethodDefs(graph, c.rd)
popOwner(c) popOwner(c)
popProcCon(c) popProcCon(c)
if c.runnableExamples != nil: testExamples(c) if c.runnableExamples != nil: testExamples(c)

View file

@ -242,9 +242,9 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
tyTypeDesc, tyGenericInvocation, tyForward: tyTypeDesc, tyGenericInvocation, tyForward:
internalError(c.info, "assignment requested for type: " & typeToString(t)) internalError(c.info, "assignment requested for type: " & typeToString(t))
of tyOrdinal, tyRange, tyInferred, of tyOrdinal, tyRange, tyInferred,
tyGenericInst, tyStatic, tyVar, tyAlias: tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink:
liftBodyAux(c, lastSon(t), body, x, y) liftBodyAux(c, lastSon(t), body, x, y)
of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("liftBodyAux") of tyUnused, tyOptAsRef: internalError("liftBodyAux")
proc newProcType(info: TLineInfo; owner: PSym): PType = proc newProcType(info: TLineInfo; owner: PSym): PType =
result = newType(tyProc, owner) result = newType(tyProc, owner)
@ -261,7 +261,7 @@ proc addParam(procType: PType; param: PSym) =
rawAddSon(procType, param.typ) rawAddSon(procType, param.typ)
proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo): PSym {.discardable.} = info: TLineInfo): PSym =
var a: TLiftCtx var a: TLiftCtx
a.info = info a.info = info
a.c = c a.c = c
@ -306,7 +306,7 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym = 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 result = t.assignment
if result.isNil: if result.isNil:
result = liftBody(c, t, attachedAsgn, info) result = liftBody(c, t, attachedAsgn, info)

View file

@ -377,7 +377,7 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
proc canDeref(n: PNode): bool {.inline.} = proc canDeref(n: PNode): bool {.inline.} =
result = n.len >= 2 and (let t = n[1].typ; 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 = proc tryDeref(n: PNode): PNode =
result = newNodeI(nkHiddenDeref, n.info) result = newNodeI(nkHiddenDeref, n.info)

View file

@ -259,19 +259,19 @@ proc makePtrType*(c: PContext, baseType: PType): PType =
proc makeTypeWithModifier*(c: PContext, proc makeTypeWithModifier*(c: PContext,
modifier: TTypeKind, modifier: TTypeKind,
baseType: PType): PType = 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 result = baseType
else: else:
result = newTypeS(modifier, c) result = newTypeS(modifier, c)
addSonSkipIntLit(result, baseType.assertNotNil) addSonSkipIntLit(result, baseType.assertNotNil)
proc makeVarType*(c: PContext, baseType: PType): PType = proc makeVarType*(c: PContext, baseType: PType; kind = tyVar): PType =
if baseType.kind == tyVar: if baseType.kind == kind:
result = baseType result = baseType
else: else:
result = newTypeS(tyVar, c) result = newTypeS(kind, c)
addSonSkipIntLit(result, baseType.assertNotNil) addSonSkipIntLit(result, baseType.assertNotNil)
proc makeTypeDesc*(c: PContext, typ: PType): PType = proc makeTypeDesc*(c: PContext, typ: PType): PType =

View file

@ -32,7 +32,7 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# XXX tyGenericInst here? # XXX tyGenericInst here?
if result.typ.kind == tyProc and tfUnresolved in result.typ.flags: if result.typ.kind == tyProc and tfUnresolved in result.typ.flags:
localError(n.info, errProcHasNoConcreteType, n.renderTree) localError(n.info, errProcHasNoConcreteType, n.renderTree)
if result.typ.kind == tyVar: result = newDeref(result) if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
elif {efWantStmt, efAllowStmt} * flags != {}: elif {efWantStmt, efAllowStmt} * flags != {}:
result.typ = newTypeS(tyVoid, c) result.typ = newTypeS(tyVoid, c)
else: else:
@ -52,7 +52,7 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result.typ = errorType(c) result.typ = errorType(c)
else: else:
if efNoProcvarCheck notin flags: semProcvarCheck(c, result) if efNoProcvarCheck notin flags: semProcvarCheck(c, result)
if result.typ.kind == tyVar: result = newDeref(result) if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semExpr(c, n, flags) result = semExpr(c, n, flags)
@ -181,9 +181,18 @@ proc semConv(c: PContext, n: PNode): PNode =
result = newNodeI(nkConv, n.info) result = newNodeI(nkConv, n.info)
var targetType = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc}) var targetType = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
maybeLiftType(targetType, c, n[0].info) maybeLiftType(targetType, c, n[0].info)
result.addSon copyTree(n.sons[0])
var op = semExprWithType(c, n.sons[1])
if targetType.kind in {tySink, tyLent}:
let baseType = semTypeNode(c, n.sons[1], nil).skipTypes({tyTypeDesc})
let t = newTypeS(targetType.kind, c)
t.rawAddSonNoPropagationOfTypeFlags baseType
result = newNodeI(nkType, n.info)
result.typ = makeTypeDesc(c, t)
return
result.addSon copyTree(n.sons[0])
var op = semExprWithType(c, n.sons[1])
if targetType.isMetaType: if targetType.isMetaType:
let final = inferWithMetatype(c, targetType, op, true) let final = inferWithMetatype(c, targetType, op, true)
result.addSon final result.addSon final
@ -191,6 +200,8 @@ proc semConv(c: PContext, n: PNode): PNode =
return return
result.typ = targetType result.typ = targetType
# XXX op is overwritten later on, this is likely added too early
# here or needs to be overwritten too then.
addSon(result, op) addSon(result, op)
if not isSymChoice(op): if not isSymChoice(op):
@ -350,7 +361,7 @@ proc changeType(n: PNode, newType: PType, check: bool) =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
changeType(n.sons[i], elemType(newType), check) changeType(n.sons[i], elemType(newType), check)
of nkPar: of nkPar:
let tup = newType.skipTypes({tyGenericInst, tyAlias}) let tup = newType.skipTypes({tyGenericInst, tyAlias, tySink})
if tup.kind != tyTuple: if tup.kind != tyTuple:
if tup.kind == tyObject: return if tup.kind == tyObject: return
globalError(n.info, "no tuple type for constructor") globalError(n.info, "no tuple type for constructor")
@ -393,7 +404,7 @@ proc arrayConstrType(c: PContext, n: PNode): PType =
if sonsLen(n) == 0: if sonsLen(n) == 0:
rawAddSon(typ, newTypeS(tyEmpty, c)) # needs an empty basetype! rawAddSon(typ, newTypeS(tyEmpty, c)) # needs an empty basetype!
else: else:
var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyOrdinal, tyAlias}) var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
addSonSkipIntLit(typ, t) addSonSkipIntLit(typ, t)
typ.sons[0] = makeRangeType(c, 0, sonsLen(n) - 1, n.info) typ.sons[0] = makeRangeType(c, 0, sonsLen(n) - 1, n.info)
result = typ result = typ
@ -417,7 +428,7 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
let yy = semExprWithType(c, x) let yy = semExprWithType(c, x)
var typ = yy.typ var typ = yy.typ
addSon(result, yy) addSon(result, yy)
#var typ = skipTypes(result.sons[0].typ, {tyGenericInst, tyVar, tyOrdinal}) #var typ = skipTypes(result.sons[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal})
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
x = n.sons[i] x = n.sons[i]
if x.kind == nkExprColonExpr and sonsLen(x) == 2: if x.kind == nkExprColonExpr and sonsLen(x) == 2:
@ -471,7 +482,7 @@ proc analyseIfAddressTaken(c: PContext, n: PNode): PNode =
of nkSym: of nkSym:
# n.sym.typ can be nil in 'check' mode ... # n.sym.typ can be nil in 'check' mode ...
if n.sym.typ != nil and if n.sym.typ != nil and
skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind != tyVar: skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
incl(n.sym.flags, sfAddrTaken) incl(n.sym.flags, sfAddrTaken)
result = newHiddenAddrTaken(c, n) result = newHiddenAddrTaken(c, n)
of nkDotExpr: of nkDotExpr:
@ -479,12 +490,12 @@ proc analyseIfAddressTaken(c: PContext, n: PNode): PNode =
if n.sons[1].kind != nkSym: if n.sons[1].kind != nkSym:
internalError(n.info, "analyseIfAddressTaken") internalError(n.info, "analyseIfAddressTaken")
return return
if skipTypes(n.sons[1].sym.typ, abstractInst-{tyTypeDesc}).kind != tyVar: if skipTypes(n.sons[1].sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
incl(n.sons[1].sym.flags, sfAddrTaken) incl(n.sons[1].sym.flags, sfAddrTaken)
result = newHiddenAddrTaken(c, n) result = newHiddenAddrTaken(c, n)
of nkBracketExpr: of nkBracketExpr:
checkMinSonsLen(n, 1) checkMinSonsLen(n, 1)
if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind != tyVar: if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
if n.sons[0].kind == nkSym: incl(n.sons[0].sym.flags, sfAddrTaken) if n.sons[0].kind == nkSym: incl(n.sons[0].sym.flags, sfAddrTaken)
result = newHiddenAddrTaken(c, n) result = newHiddenAddrTaken(c, n)
else: else:
@ -499,7 +510,7 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
# get the real type of the callee # get the real type of the callee
# it may be a proc var with a generic alias type, so we skip over them # it may be a proc var with a generic alias type, so we skip over them
var t = n.sons[0].typ.skipTypes({tyGenericInst, tyAlias}) var t = n.sons[0].typ.skipTypes({tyGenericInst, tyAlias, tySink})
if n.sons[0].kind == nkSym and n.sons[0].sym.magic in FakeVarParams: if n.sons[0].kind == nkSym and n.sons[0].sym.magic in FakeVarParams:
# BUGFIX: check for L-Value still needs to be done for the arguments! # BUGFIX: check for L-Value still needs to be done for the arguments!
@ -692,7 +703,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
else: else:
n.sons[0] = semExpr(c, n.sons[0], {efInCall}) n.sons[0] = semExpr(c, n.sons[0], {efInCall})
let t = n.sons[0].typ let t = n.sons[0].typ
if t != nil and t.kind == tyVar: if t != nil and t.kind in {tyVar, tyLent}:
n.sons[0] = newDeref(n.sons[0]) n.sons[0] = newDeref(n.sons[0])
elif n.sons[0].kind == nkBracketExpr: elif n.sons[0].kind == nkBracketExpr:
let s = bracketedMacro(n.sons[0]) let s = bracketedMacro(n.sons[0])
@ -780,6 +791,19 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode =
proc semExprNoType(c: PContext, n: PNode): PNode = proc semExprNoType(c: PContext, n: PNode): PNode =
result = semExpr(c, n, {efWantStmt}) result = semExpr(c, n, {efWantStmt})
# make an 'if' expression an 'if' statement again for backwards
# compatibility (.discardable was a bad idea!); bug #6980
var isStmt = false
if result.kind == nkIfExpr:
isStmt = true
for condActionPair in result:
let action = condActionPair.lastSon
if not implicitlyDiscardable(action) and not
endsInNoReturn(action):
isStmt = false
if isStmt:
result.kind = nkIfStmt
result.typ = nil
discardCheck(c, result) discardCheck(c, result)
proc isTypeExpr(n: PNode): bool = proc isTypeExpr(n: PNode): bool =
@ -852,7 +876,7 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
const const
tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass} tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass}
tyDotOpTransparent = {tyVar, tyPtr, tyRef, tyAlias} tyDotOpTransparent = {tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink}
proc readTypeParameter(c: PContext, typ: PType, proc readTypeParameter(c: PContext, typ: PType,
paramName: PIdent, info: TLineInfo): PNode = paramName: PIdent, info: TLineInfo): PNode =
@ -927,7 +951,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
else: else:
result = semMacroExpr(c, n, n, s, flags) result = semMacroExpr(c, n, n, s, flags)
of skTemplate: of skTemplate:
if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0: if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or
sfCustomPragma in sym.flags:
markUsed(n.info, s, c.graph.usageSym) markUsed(n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s) styleCheckUse(n.info, s)
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
@ -985,8 +1010,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
while p != nil and p.selfSym == nil: while p != nil and p.selfSym == nil:
p = p.next p = p.next
if p != nil and p.selfSym != nil: if p != nil and p.selfSym != nil:
var ty = skipTypes(p.selfSym.typ, {tyGenericInst, tyVar, tyPtr, tyRef, var ty = skipTypes(p.selfSym.typ, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef,
tyAlias}) tyAlias, tySink})
while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct}) while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct})
var check: PNode = nil var check: PNode = nil
if ty.kind == tyObject: if ty.kind == tyObject:
@ -1095,7 +1120,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
return nil return nil
if ty.kind in tyUserTypeClasses and ty.isResolvedUserTypeClass: if ty.kind in tyUserTypeClasses and ty.isResolvedUserTypeClass:
ty = ty.lastSon ty = ty.lastSon
ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef, tyAlias}) ty = skipTypes(ty, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct}) while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct})
var check: PNode = nil var check: PNode = nil
if ty.kind == tyObject: if ty.kind == tyObject:
@ -1162,7 +1187,7 @@ proc semDeref(c: PContext, n: PNode): PNode =
checkSonsLen(n, 1) checkSonsLen(n, 1)
n.sons[0] = semExprWithType(c, n.sons[0]) n.sons[0] = semExprWithType(c, n.sons[0])
result = n result = n
var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyAlias}) var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink})
case t.kind case t.kind
of tyRef, tyPtr: n.typ = t.lastSon of tyRef, tyPtr: n.typ = t.lastSon
else: result = nil else: result = nil
@ -1182,7 +1207,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
n.sons[0] = semExprWithType(c, n.sons[0], n.sons[0] = semExprWithType(c, n.sons[0],
{efNoProcvarCheck, efNoEvaluateGeneric}) {efNoProcvarCheck, efNoEvaluateGeneric})
let arr = skipTypes(n.sons[0].typ, {tyGenericInst, let arr = skipTypes(n.sons[0].typ, {tyGenericInst,
tyVar, tyPtr, tyRef, tyAlias}) tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
case arr.kind case arr.kind
of tyArray, tyOpenArray, tyVarargs, tySequence, tyString, of tyArray, tyOpenArray, tyVarargs, tySequence, tyString,
tyCString: tyCString:
@ -1210,7 +1235,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
n.sons[0] = makeDeref(n.sons[0]) n.sons[0] = makeDeref(n.sons[0])
# [] operator for tuples requires constant expression: # [] operator for tuples requires constant expression:
n.sons[1] = semConstExpr(c, n.sons[1]) n.sons[1] = semConstExpr(c, n.sons[1])
if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal, tyAlias}).kind in if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal, tyAlias, tySink}).kind in
{tyInt..tyInt64}: {tyInt..tyInt64}:
var idx = getOrdValue(n.sons[1]) var idx = getOrdValue(n.sons[1])
if idx >= 0 and idx < sonsLen(arr): n.typ = arr.sons[int(idx)] if idx >= 0 and idx < sonsLen(arr): n.typ = arr.sons[int(idx)]
@ -1290,13 +1315,10 @@ proc takeImplicitAddr(c: PContext, n: PNode): PNode =
proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
if le.kind == nkHiddenDeref: if le.kind == nkHiddenDeref:
var x = le.sons[0] var x = le.sons[0]
if x.typ.kind == tyVar and x.kind == nkSym: if x.typ.kind == tyVar and x.kind == nkSym and x.sym.kind == skResult:
if x.sym.kind == skResult: n.sons[0] = x # 'result[]' --> 'result'
n.sons[0] = x # 'result[]' --> 'result' n.sons[1] = takeImplicitAddr(c, ri)
n.sons[1] = takeImplicitAddr(c, ri) x.typ.flags.incl tfVarIsPtr
if x.sym.kind != skParam:
# XXX This is hacky. See bug #4910.
x.typ.flags.incl tfVarIsPtr
#echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info #echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info
template resultTypeIsInferrable(typ: PType): untyped = template resultTypeIsInferrable(typ: PType): untyped =
@ -1352,7 +1374,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
# a = b # both are vars, means: a[] = b[] # a = b # both are vars, means: a[] = b[]
# a = b # b no 'var T' means: a = addr(b) # a = b # b no 'var T' means: a = addr(b)
var le = a.typ var le = a.typ
if (skipTypes(le, {tyGenericInst, tyAlias}).kind != tyVar and if (skipTypes(le, {tyGenericInst, tyAlias, tySink}).kind != tyVar and
isAssignable(c, a) == arNone) or isAssignable(c, a) == arNone) or
skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs}: skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs}:
# Direct assignment to a discriminant is allowed! # Direct assignment to a discriminant is allowed!
@ -1368,13 +1390,16 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
if lhsIsResult: if lhsIsResult:
n.typ = enforceVoidContext n.typ = enforceVoidContext
if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ): if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ):
if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric: var rhsTyp = rhs.typ
if rhsTyp.kind in tyUserTypeClasses and rhsTyp.isResolvedUserTypeClass:
rhsTyp = rhsTyp.lastSon
if cmpTypes(c, lhs.typ, rhsTyp) in {isGeneric, isEqual}:
internalAssert c.p.resultSym != nil internalAssert c.p.resultSym != nil
lhs.typ = rhs.typ lhs.typ = rhsTyp
c.p.resultSym.typ = rhs.typ c.p.resultSym.typ = rhsTyp
c.p.owner.typ.sons[0] = rhs.typ c.p.owner.typ.sons[0] = rhsTyp
else: else:
typeMismatch(n.info, lhs.typ, rhs.typ) typeMismatch(n.info, lhs.typ, rhsTyp)
n.sons[1] = fitNode(c, le, rhs, n.info) n.sons[1] = fitNode(c, le, rhs, n.info)
if not newDestructors: if not newDestructors:
@ -1412,11 +1437,7 @@ proc semProcBody(c: PContext, n: PNode): PNode =
openScope(c) openScope(c)
result = semExpr(c, n) result = semExpr(c, n)
if c.p.resultSym != nil and not isEmptyType(result.typ): if c.p.resultSym != nil and not isEmptyType(result.typ):
# transform ``expr`` to ``result = expr``, but not if the expr is already if result.kind == nkNilLit:
# ``result``:
if result.kind == nkSym and result.sym == c.p.resultSym:
discard
elif result.kind == nkNilLit:
# or ImplicitlyDiscardable(result): # or ImplicitlyDiscardable(result):
# new semantic: 'result = x' triggers the void context # new semantic: 'result = x' triggers the void context
result.typ = nil result.typ = nil
@ -1446,17 +1467,18 @@ proc semProcBody(c: PContext, n: PNode): PNode =
closeScope(c) closeScope(c)
proc semYieldVarResult(c: PContext, n: PNode, restype: PType) = proc semYieldVarResult(c: PContext, n: PNode, restype: PType) =
var t = skipTypes(restype, {tyGenericInst, tyAlias}) var t = skipTypes(restype, {tyGenericInst, tyAlias, tySink})
case t.kind case t.kind
of tyVar: of tyVar, tyLent:
if t.kind == tyVar: t.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892
if n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv}: if n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv}:
n.sons[0] = n.sons[0].sons[1] n.sons[0] = n.sons[0].sons[1]
n.sons[0] = takeImplicitAddr(c, n.sons[0]) n.sons[0] = takeImplicitAddr(c, n.sons[0])
of tyTuple: of tyTuple:
for i in 0..<t.sonsLen: for i in 0..<t.sonsLen:
var e = skipTypes(t.sons[i], {tyGenericInst, tyAlias}) var e = skipTypes(t.sons[i], {tyGenericInst, tyAlias, tySink})
if e.kind == tyVar: if e.kind in {tyVar, tyLent}:
if e.kind == tyVar: e.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892
if n.sons[0].kind == nkPar: if n.sons[0].kind == nkPar:
n.sons[0].sons[i] = takeImplicitAddr(c, n.sons[0].sons[i]) n.sons[0].sons[i] = takeImplicitAddr(c, n.sons[0].sons[i])
elif n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv} and elif n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv} and
@ -1775,6 +1797,13 @@ proc setMs(n: PNode, s: PSym): PNode =
n.sons[0] = newSymNode(s) n.sons[0] = newSymNode(s)
n.sons[0].info = n.info n.sons[0].info = n.info
proc extractImports(n: PNode; result: PNode) =
if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}:
result.add copyTree(n)
n.kind = nkEmpty
return
for i in 0..<n.safeLen: extractImports(n[i], result)
proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
# this is a hotspot in the compiler! # this is a hotspot in the compiler!
# DON'T forget to update ast.SpecialSemMagics if you add a magic here! # DON'T forget to update ast.SpecialSemMagics if you add a magic here!
@ -1848,14 +1877,16 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
result = magicsAfterOverloadResolution(c, result, flags) result = magicsAfterOverloadResolution(c, result, flags)
of mRunnableExamples: of mRunnableExamples:
if gCmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList: if gCmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList:
if n.sons[0].kind == nkIdent: if sfMainModule in c.module.flags:
if sfMainModule in c.module.flags: let inp = toFullPath(c.module.info)
let inp = toFullPath(c.module.info) if c.runnableExamples == nil:
if c.runnableExamples == nil: c.runnableExamples = newTree(nkStmtList,
c.runnableExamples = newTree(nkStmtList, newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp))))
newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp)))) let imports = newTree(nkStmtList)
c.runnableExamples.add newTree(nkBlockStmt, emptyNode, copyTree n.lastSon) extractImports(n.lastSon, imports)
result = setMs(n, s) for imp in imports: c.runnableExamples.add imp
c.runnableExamples.add newTree(nkBlockStmt, emptyNode, copyTree n.lastSon)
result = setMs(n, s)
else: else:
result = emptyNode result = emptyNode
else: else:
@ -1936,17 +1967,17 @@ proc semSetConstr(c: PContext, n: PNode): PNode =
n.sons[i].sons[2] = semExprWithType(c, n.sons[i].sons[2]) n.sons[i].sons[2] = semExprWithType(c, n.sons[i].sons[2])
if typ == nil: if typ == nil:
typ = skipTypes(n.sons[i].sons[1].typ, typ = skipTypes(n.sons[i].sons[1].typ,
{tyGenericInst, tyVar, tyOrdinal, tyAlias}) {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
n.sons[i].typ = n.sons[i].sons[2].typ # range node needs type too n.sons[i].typ = n.sons[i].sons[2].typ # range node needs type too
elif n.sons[i].kind == nkRange: elif n.sons[i].kind == nkRange:
# already semchecked # already semchecked
if typ == nil: if typ == nil:
typ = skipTypes(n.sons[i].sons[0].typ, typ = skipTypes(n.sons[i].sons[0].typ,
{tyGenericInst, tyVar, tyOrdinal, tyAlias}) {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
else: else:
n.sons[i] = semExprWithType(c, n.sons[i]) n.sons[i] = semExprWithType(c, n.sons[i])
if typ == nil: if typ == nil:
typ = skipTypes(n.sons[i].typ, {tyGenericInst, tyVar, tyOrdinal, tyAlias}) typ = skipTypes(n.sons[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
if not isOrdinalType(typ): if not isOrdinalType(typ):
localError(n.info, errOrdinalTypeExpected) localError(n.info, errOrdinalTypeExpected)
typ = makeRangeType(c, 0, MaxSetElements-1, n.info) typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
@ -2123,6 +2154,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkIdent, nkAccQuoted: of nkIdent, nkAccQuoted:
let checks = if efNoEvaluateGeneric in flags: let checks = if efNoEvaluateGeneric in flags:
{checkUndeclared, checkPureEnumFields} {checkUndeclared, checkPureEnumFields}
elif efInCall in flags:
{checkUndeclared, checkModule, checkPureEnumFields}
else: else:
{checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields} {checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields}
var s = qualifiedLookUp(c, n, checks) var s = qualifiedLookUp(c, n, checks)
@ -2217,10 +2250,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# XXX think about this more (``set`` procs) # XXX think about this more (``set`` procs)
if n.len == 2: if n.len == 2:
result = semConv(c, n) result = semConv(c, n)
elif contains(c.ambiguousSymbols, s.id) and n.len == 1:
errorUseQualifier(c, n.info, s)
elif n.len == 1: elif n.len == 1:
result = semObjConstr(c, n, flags) result = semObjConstr(c, n, flags)
elif contains(c.ambiguousSymbols, s.id):
errorUseQualifier(c, n.info, s)
elif s.magic == mNone: result = semDirectOp(c, n, flags) elif s.magic == mNone: result = semDirectOp(c, n, flags)
else: result = semMagic(c, n, s, flags) else: result = semMagic(c, n, s, flags)
of skProc, skFunc, skMethod, skConverter, skIterator: of skProc, skFunc, skMethod, skConverter, skIterator:
@ -2370,6 +2403,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
if n.len != 1 and n.len != 2: illFormedAst(n) if n.len != 1 and n.len != 2: illFormedAst(n)
for i in 0 ..< n.len: for i in 0 ..< n.len:
n.sons[i] = semExpr(c, n.sons[i]) n.sons[i] = semExpr(c, n.sons[i])
of nkComesFrom: discard "ignore the comes from information for now"
else: else:
localError(n.info, errInvalidExpressionX, localError(n.info, errInvalidExpressionX,
renderTree(n, {renderNoComments})) renderTree(n, {renderNoComments}))

View file

@ -408,7 +408,7 @@ proc getArrayConstr(m: PSym, n: PNode): PNode =
proc foldArrayAccess(m: PSym, n: PNode): PNode = proc foldArrayAccess(m: PSym, n: PNode): PNode =
var x = getConstExpr(m, n.sons[0]) var x = getConstExpr(m, n.sons[0])
if x == nil or x.typ.skipTypes({tyGenericInst, tyAlias}).kind == tyTypeDesc: if x == nil or x.typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyTypeDesc:
return return
var y = getConstExpr(m, n.sons[1]) var y = getConstExpr(m, n.sons[1])
@ -655,5 +655,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
result.typ = n.typ result.typ = n.typ
of nkBracketExpr: result = foldArrayAccess(m, n) of nkBracketExpr: result = foldArrayAccess(m, n)
of nkDotExpr: result = foldFieldAccess(m, n) of nkDotExpr: result = foldFieldAccess(m, n)
of nkStmtListExpr:
if n.len == 2 and n[0].kind == nkComesFrom:
result = getConstExpr(m, n[1])
else: else:
discard discard

View file

@ -186,7 +186,7 @@ proc semGenericStmt(c: PContext, n: PNode,
let a = n.sym let a = n.sym
let b = getGenSym(c, a) let b = getGenSym(c, a)
if b != a: n.sym = b if b != a: n.sym = b
of nkEmpty, succ(nkSym)..nkNilLit: of nkEmpty, succ(nkSym)..nkNilLit, nkComesFrom:
# see tests/compile/tgensymgeneric.nim: # see tests/compile/tgensymgeneric.nim:
# We need to open the gensym'ed symbol again so that the instantiation # We need to open the gensym'ed symbol again so that the instantiation
# creates a fresh copy; but this is wrong the very first reason for gensym # creates a fresh copy; but this is wrong the very first reason for gensym

View file

@ -108,7 +108,7 @@ proc uninstantiate(t: PType): PType =
else: t else: t
proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode = proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode =
const skippedTypes = {tyTypeDesc, tyAlias} const skippedTypes = {tyTypeDesc, tyAlias, tySink}
let trait = traitCall[0] let trait = traitCall[0]
internalAssert trait.kind == nkSym internalAssert trait.kind == nkSym
var operand = operand.skipTypes(skippedTypes) var operand = operand.skipTypes(skippedTypes)
@ -145,7 +145,7 @@ proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode =
of "stripGenericParams": of "stripGenericParams":
result = uninstantiate(operand).toNode(traitCall.info) result = uninstantiate(operand).toNode(traitCall.info)
of "supportsCopyMem": 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 let complexObj = containsGarbageCollectedRef(t) or
hasDestructor(t) hasDestructor(t)
result = newIntNodeT(ord(not complexObj), traitCall) result = newIntNodeT(ord(not complexObj), traitCall)

View file

@ -39,13 +39,19 @@ proc mergeInitStatus(existing: var InitStatus, newStatus: InitStatus) =
of initUnknown: of initUnknown:
discard discard
proc invalidObjConstr(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")
else:
localError(n.info, "incorrect object construction syntax")
proc locateFieldInInitExpr(field: PSym, initExpr: PNode): PNode = proc locateFieldInInitExpr(field: PSym, initExpr: PNode): PNode =
# Returns the assignment nkExprColonExpr node or nil # Returns the assignment nkExprColonExpr node or nil
let fieldId = field.name.id let fieldId = field.name.id
for i in 1 ..< initExpr.len: for i in 1 ..< initExpr.len:
let assignment = initExpr[i] let assignment = initExpr[i]
if assignment.kind != nkExprColonExpr: if assignment.kind != nkExprColonExpr:
localError(initExpr.info, "incorrect object construction syntax") invalidObjConstr(assignment)
continue continue
if fieldId == considerQuotedIdent(assignment[0]).id: if fieldId == considerQuotedIdent(assignment[0]).id:
@ -254,8 +260,8 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = newNodeIT(nkObjConstr, n.info, t) result = newNodeIT(nkObjConstr, n.info, t)
for child in n: result.add child for child in n: result.add child
t = skipTypes(t, {tyGenericInst, tyAlias}) t = skipTypes(t, {tyGenericInst, tyAlias, tySink})
if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias}) if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias, tySink})
if t.kind != tyObject: if t.kind != tyObject:
localError(n.info, errGenerated, "object constructor needs an object type") localError(n.info, errGenerated, "object constructor needs an object type")
return return
@ -284,7 +290,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
let field = result[i] let field = result[i]
if nfSem notin field.flags: if nfSem notin field.flags:
if field.kind != nkExprColonExpr: if field.kind != nkExprColonExpr:
localError(n.info, "incorrect object construction syntax") invalidObjConstr(field)
continue continue
let id = considerQuotedIdent(field[0]) let id = considerQuotedIdent(field[0])
# This node was not processed. There are two possible reasons: # This node was not processed. There are two possible reasons:

View file

@ -979,10 +979,10 @@ proc trackProc*(s: PSym, body: PNode) =
message(s.info, warnLockLevel, message(s.info, warnLockLevel,
"declared lock level is $1, but real lock level is $2" % "declared lock level is $1, but real lock level is $2" %
[$s.typ.lockLevel, $t.maxLockLevel]) [$s.typ.lockLevel, $t.maxLockLevel])
when false: when defined(useDfa):
if s.kind == skFunc: if s.kind == skFunc:
when defined(dfa): dataflowAnalysis(s, body) dataflowAnalysis(s, body)
trackWrites(s, body) when false: trackWrites(s, body)
proc trackTopLevelStmt*(module: PSym; n: PNode) = proc trackTopLevelStmt*(module: PSym; n: PNode) =
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef, if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,

View file

@ -102,7 +102,7 @@ proc semExprBranch(c: PContext, n: PNode): PNode =
if result.typ != nil: if result.typ != nil:
# XXX tyGenericInst here? # XXX tyGenericInst here?
semProcvarCheck(c, result) semProcvarCheck(c, result)
if result.typ.kind == tyVar: result = newDeref(result) if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
proc semExprBranchScope(c: PContext, n: PNode): PNode = proc semExprBranchScope(c: PContext, n: PNode): PNode =
openScope(c) openScope(c)
@ -165,14 +165,14 @@ proc semIf(c: PContext, n: PNode): PNode =
it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0])) it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
when not newScopeForIf: openScope(c) when not newScopeForIf: openScope(c)
it.sons[1] = semExprBranch(c, it.sons[1]) it.sons[1] = semExprBranch(c, it.sons[1])
typ = commonType(typ, it.sons[1].typ) typ = commonType(typ, it.sons[1])
closeScope(c) closeScope(c)
elif it.len == 1: elif it.len == 1:
hasElse = true hasElse = true
it.sons[0] = semExprBranchScope(c, it.sons[0]) it.sons[0] = semExprBranchScope(c, it.sons[0])
typ = commonType(typ, it.sons[0].typ) typ = commonType(typ, it.sons[0])
else: illFormedAst(it) else: illFormedAst(it)
if isEmptyType(typ) or typ.kind == tyNil or not hasElse: if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
for it in n: discardCheck(c, it.lastSon) for it in n: discardCheck(c, it.lastSon)
result.kind = nkIfStmt result.kind = nkIfStmt
# propagate any enforced VoidContext: # propagate any enforced VoidContext:
@ -180,7 +180,8 @@ proc semIf(c: PContext, n: PNode): PNode =
else: else:
for it in n: for it in n:
let j = it.len-1 let j = it.len-1
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info) if not endsInNoReturn(it.sons[j]):
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
result.kind = nkIfExpr result.kind = nkIfExpr
result.typ = typ result.typ = typ
@ -213,7 +214,7 @@ proc semCase(c: PContext, n: PNode): PNode =
semCaseBranch(c, n, x, i, covered) semCaseBranch(c, n, x, i, covered)
var last = sonsLen(x)-1 var last = sonsLen(x)-1
x.sons[last] = semExprBranchScope(c, x.sons[last]) x.sons[last] = semExprBranchScope(c, x.sons[last])
typ = commonType(typ, x.sons[last].typ) typ = commonType(typ, x.sons[last])
of nkElifBranch: of nkElifBranch:
chckCovered = false chckCovered = false
checkSonsLen(x, 2) checkSonsLen(x, 2)
@ -221,13 +222,13 @@ proc semCase(c: PContext, n: PNode): PNode =
x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0])) x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
when not newScopeForIf: openScope(c) when not newScopeForIf: openScope(c)
x.sons[1] = semExprBranch(c, x.sons[1]) x.sons[1] = semExprBranch(c, x.sons[1])
typ = commonType(typ, x.sons[1].typ) typ = commonType(typ, x.sons[1])
closeScope(c) closeScope(c)
of nkElse: of nkElse:
chckCovered = false chckCovered = false
checkSonsLen(x, 1) checkSonsLen(x, 1)
x.sons[0] = semExprBranchScope(c, x.sons[0]) x.sons[0] = semExprBranchScope(c, x.sons[0])
typ = commonType(typ, x.sons[0].typ) typ = commonType(typ, x.sons[0])
hasElse = true hasElse = true
else: else:
illFormedAst(x) illFormedAst(x)
@ -237,7 +238,7 @@ proc semCase(c: PContext, n: PNode): PNode =
else: else:
localError(n.info, errNotAllCasesCovered) localError(n.info, errNotAllCasesCovered)
closeScope(c) closeScope(c)
if isEmptyType(typ) or typ.kind == tyNil or not hasElse: if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon) for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
# propagate any enforced VoidContext: # propagate any enforced VoidContext:
if typ == enforceVoidContext: if typ == enforceVoidContext:
@ -246,7 +247,8 @@ proc semCase(c: PContext, n: PNode): PNode =
for i in 1..n.len-1: for i in 1..n.len-1:
var it = n.sons[i] var it = n.sons[i]
let j = it.len-1 let j = it.len-1
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info) if not endsInNoReturn(it.sons[j]):
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
result.typ = typ result.typ = typ
proc semTry(c: PContext, n: PNode): PNode = proc semTry(c: PContext, n: PNode): PNode =
@ -441,20 +443,21 @@ proc hasEmpty(typ: PType): bool =
result = result or hasEmpty(s) result = result or hasEmpty(s)
proc makeDeref(n: PNode): PNode = proc makeDeref(n: PNode): PNode =
var t = skipTypes(n.typ, {tyGenericInst, tyAlias}) var t = n.typ
if t.kind in tyUserTypeClasses and t.isResolvedUserTypeClass: if t.kind in tyUserTypeClasses and t.isResolvedUserTypeClass:
t = t.lastSon t = t.lastSon
t = skipTypes(t, {tyGenericInst, tyAlias, tySink})
result = n result = n
if t.kind == tyVar: if t.kind in {tyVar, tyLent}:
result = newNodeIT(nkHiddenDeref, n.info, t.sons[0]) result = newNodeIT(nkHiddenDeref, n.info, t.sons[0])
addSon(result, n) addSon(result, n)
t = skipTypes(t.sons[0], {tyGenericInst, tyAlias}) t = skipTypes(t.sons[0], {tyGenericInst, tyAlias, tySink})
while t.kind in {tyPtr, tyRef}: while t.kind in {tyPtr, tyRef}:
var a = result var a = result
let baseTyp = t.lastSon let baseTyp = t.lastSon
result = newNodeIT(nkHiddenDeref, n.info, baseTyp) result = newNodeIT(nkHiddenDeref, n.info, baseTyp)
addSon(result, a) addSon(result, a)
t = skipTypes(baseTyp, {tyGenericInst, tyAlias}) t = skipTypes(baseTyp, {tyGenericInst, tyAlias, tySink})
proc fillPartialObject(c: PContext; n: PNode; typ: PType) = proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
if n.len == 2: if n.len == 2:
@ -530,7 +533,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
if typ == nil: continue if typ == nil: continue
typeAllowedCheck(a.info, typ, symkind) typeAllowedCheck(a.info, typ, symkind)
liftTypeBoundOps(c, typ, a.info) liftTypeBoundOps(c, typ, a.info)
var tup = skipTypes(typ, {tyGenericInst, tyAlias}) var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
if a.kind == nkVarTuple: if a.kind == nkVarTuple:
if tup.kind != tyTuple: if tup.kind != tyTuple:
localError(a.info, errXExpected, "tuple") localError(a.info, errXExpected, "tuple")
@ -646,7 +649,7 @@ proc semForVars(c: PContext, n: PNode): PNode =
result = n result = n
var length = sonsLen(n) var length = sonsLen(n)
let iterBase = n.sons[length-2].typ let iterBase = n.sons[length-2].typ
var iter = skipTypes(iterBase, {tyGenericInst, tyAlias}) var iter = skipTypes(iterBase, {tyGenericInst, tyAlias, tySink})
# length == 3 means that there is one for loop variable # length == 3 means that there is one for loop variable
# and thus no tuple unpacking: # and thus no tuple unpacking:
if iter.kind != tyTuple or length == 3: if iter.kind != tyTuple or length == 3:
@ -680,7 +683,7 @@ proc semForVars(c: PContext, n: PNode): PNode =
proc implicitIterator(c: PContext, it: string, arg: PNode): PNode = proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
result = newNodeI(nkCall, arg.info) result = newNodeI(nkCall, arg.info)
result.add(newIdentNode(it.getIdent, arg.info)) result.add(newIdentNode(it.getIdent, arg.info))
if arg.typ != nil and arg.typ.kind == tyVar: if arg.typ != nil and arg.typ.kind in {tyVar, tyLent}:
result.add newDeref(arg) result.add newDeref(arg)
else: else:
result.add arg result.add arg
@ -730,6 +733,18 @@ proc semRaise(c: PContext, n: PNode): PNode =
if typ.kind != tyRef or typ.lastSon.kind != tyObject: if typ.kind != tyRef or typ.lastSon.kind != tyObject:
localError(n.info, errExprCannotBeRaised) localError(n.info, errExprCannotBeRaised)
# check if the given object inherits from Exception
var base = typ.lastSon
while true:
if base.sym.magic == mException:
break
if base.lastSon == nil:
localError(n.info,
"raised object of type $1 does not inherit from Exception",
[typeToString(typ)])
return
base = base.lastSon
proc addGenericParamListToScope(c: PContext, n: PNode) = proc addGenericParamListToScope(c: PContext, n: PNode) =
if n.kind != nkGenericParams: illFormedAst(n) if n.kind != nkGenericParams: illFormedAst(n)
for i in countup(0, sonsLen(n)-1): for i in countup(0, sonsLen(n)-1):
@ -865,11 +880,11 @@ proc checkCovariantParamsUsages(genericType: PType) =
for fieldType in t.sons: for fieldType in t.sons:
subresult traverseSubTypes(fieldType) subresult traverseSubTypes(fieldType)
of tyPtr, tyRef, tyVar: of tyPtr, tyRef, tyVar, tyLent:
if t.base.kind == tyGenericParam: return true if t.base.kind == tyGenericParam: return true
return traverseSubTypes(t.base) return traverseSubTypes(t.base)
of tyDistinct, tyAlias: of tyDistinct, tyAlias, tySink:
return traverseSubTypes(t.lastSon) return traverseSubTypes(t.lastSon)
of tyGenericInst: of tyGenericInst:
@ -979,8 +994,8 @@ proc checkForMetaFields(n: PNode) =
of nkSym: of nkSym:
let t = n.sym.typ let t = n.sym.typ
case t.kind case t.kind
of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef, of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyLent, tyPtr, tyRef,
tyProc, tyGenericInvocation, tyGenericInst, tyAlias: tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink:
let start = int ord(t.kind in {tyGenericInvocation, tyGenericInst}) let start = int ord(t.kind in {tyGenericInvocation, tyGenericInst})
for i in start ..< t.sons.len: for i in start ..< t.sons.len:
checkMeta(t.sons[i]) checkMeta(t.sons[i])
@ -1005,7 +1020,7 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
# type aliases are hard: # type aliases are hard:
var t = semTypeNode(c, x, nil) var t = semTypeNode(c, x, nil)
assert t != nil assert t != nil
if s.typ != nil and s.typ.kind != tyAlias: if s.typ != nil and s.typ.kind notin {tyAlias, tySink}:
if t.kind in {tyProc, tyGenericInst} and not t.isMetaType: if t.kind in {tyProc, tyGenericInst} and not t.isMetaType:
assignType(s.typ, t) assignType(s.typ, t)
s.typ.id = t.id s.typ.id = t.id
@ -1138,6 +1153,9 @@ proc semProcAnnotation(c: PContext, prc: PNode;
else: else:
localError(prc.info, errOnlyACallOpCanBeDelegator) localError(prc.info, errOnlyACallOpCanBeDelegator)
continue continue
elif sfCustomPragma in m.flags:
continue # semantic check for custom pragma happens later in semProcAux
# we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and # we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and
# let the semantic checker deal with it: # let the semantic checker deal with it:
var x = newNodeI(nkCall, n.info) var x = newNodeI(nkCall, n.info)
@ -1400,9 +1418,9 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
for col in countup(1, sonsLen(tt)-1): for col in countup(1, sonsLen(tt)-1):
let t = tt.sons[col] let t = tt.sons[col]
if t != nil and t.kind == tyGenericInvocation: if t != nil and t.kind == tyGenericInvocation:
var x = skipTypes(t.sons[0], {tyVar, tyPtr, tyRef, tyGenericInst, var x = skipTypes(t.sons[0], {tyVar, tyLent, tyPtr, tyRef, tyGenericInst,
tyGenericInvocation, tyGenericBody, tyGenericInvocation, tyGenericBody,
tyAlias}) tyAlias, tySink})
if x.kind == tyObject and t.len-1 == n.sons[genericParamsPos].len: if x.kind == tyObject and t.len-1 == n.sons[genericParamsPos].len:
foundObj = true foundObj = true
x.methods.safeAdd((col,s)) x.methods.safeAdd((col,s))
@ -1840,8 +1858,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
else: else:
n.typ = n.sons[i].typ n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr if not isEmptyType(n.typ): n.kind = nkStmtListExpr
case n.sons[i].kind if n.sons[i].kind in LastBlockStmts or
of LastBlockStmts: n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and sfNoReturn in n.sons[i][0].sym.flags:
for j in countup(i + 1, length - 1): for j in countup(i + 1, length - 1):
case n.sons[j].kind case n.sons[j].kind
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr, of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,

View file

@ -331,7 +331,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
of nkMixinStmt: of nkMixinStmt:
if c.scopeN > 0: result = semTemplBodySons(c, n) if c.scopeN > 0: result = semTemplBodySons(c, n)
else: result = semMixinStmt(c.c, n, c.toMixin) else: result = semMixinStmt(c.c, n, c.toMixin)
of nkEmpty, nkSym..nkNilLit: of nkEmpty, nkSym..nkNilLit, nkComesFrom:
discard discard
of nkIfStmt: of nkIfStmt:
for i in countup(0, sonsLen(n)-1): for i in countup(0, sonsLen(n)-1):
@ -528,7 +528,7 @@ proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
result = semTemplBodyDirty(c, n.sons[0]) result = semTemplBodyDirty(c, n.sons[0])
of nkBindStmt: of nkBindStmt:
result = semBindStmt(c.c, n, c.toBind) result = semBindStmt(c.c, n, c.toBind)
of nkEmpty, nkSym..nkNilLit: of nkEmpty, nkSym..nkNilLit, nkComesFrom:
discard discard
else: else:
# dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam', # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
@ -608,7 +608,10 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
popOwner(c) popOwner(c)
s.ast = n s.ast = n
result = n result = n
if n.sons[bodyPos].kind == nkEmpty: if sfCustomPragma in s.flags:
if n.sons[bodyPos].kind != nkEmpty:
localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
elif n.sons[bodyPos].kind == nkEmpty:
localError(n.info, errImplOfXexpected, s.name.s) localError(n.info, errImplOfXexpected, s.name.s)
var proto = searchForProc(c, c.currentScope, s) var proto = searchForProc(c, c.currentScope, s)
if proto == nil: if proto == nil:

View file

@ -101,7 +101,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
if sonsLen(n) == 2: if sonsLen(n) == 2:
var base = semTypeNode(c, n.sons[1], nil) var base = semTypeNode(c, n.sons[1], nil)
addSonSkipIntLit(result, base) addSonSkipIntLit(result, base)
if base.kind in {tyGenericInst, tyAlias}: base = lastSon(base) if base.kind in {tyGenericInst, tyAlias, tySink}: base = lastSon(base)
if base.kind != tyGenericParam: if base.kind != tyGenericParam:
if not isOrdinalType(base): if not isOrdinalType(base):
localError(n.info, errOrdinalTypeExpected) localError(n.info, errOrdinalTypeExpected)
@ -153,7 +153,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
isNilable = true isNilable = true
else: else:
let region = semTypeNode(c, ni, nil) let region = semTypeNode(c, ni, nil)
if region.skipTypes({tyGenericInst, tyAlias}).kind notin { if region.skipTypes({tyGenericInst, tyAlias, tySink}).kind notin {
tyError, tyObject}: tyError, tyObject}:
message n[i].info, errGenerated, "region needs to be an object type" message n[i].info, errGenerated, "region needs to be an object type"
addSonSkipIntLit(result, region) addSonSkipIntLit(result, region)
@ -286,7 +286,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
# 3 = length(array indx base) # 3 = length(array indx base)
let indx = semArrayIndex(c, n[1]) let indx = semArrayIndex(c, n[1])
var indxB = indx var indxB = indx
if indxB.kind in {tyGenericInst, tyAlias}: indxB = lastSon(indxB) if indxB.kind in {tyGenericInst, tyAlias, tySink}: indxB = lastSon(indxB)
if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr}: if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr}:
if not isOrdinalType(indxB): if not isOrdinalType(indxB):
localError(n.sons[1].info, errOrdinalTypeExpected) localError(n.sons[1].info, errOrdinalTypeExpected)
@ -320,11 +320,8 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
if n.kind == nkSym: if n.kind == nkSym:
result = getGenSym(c, n.sym) result = getGenSym(c, n.sym)
else: else:
when defined(nimfix): result = pickSym(c, n, {skType, skGenericParam})
result = pickSym(c, n, skType) if result.isNil:
if result.isNil:
result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared})
else:
result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared})
if result != nil: if result != nil:
markUsed(n.info, result, c.graph.usageSym) markUsed(n.info, result, c.graph.usageSym)
@ -676,7 +673,7 @@ proc skipGenericInvocation(t: PType): PType {.inline.} =
result = t result = t
if result.kind == tyGenericInvocation: if result.kind == tyGenericInvocation:
result = result.sons[0] result = result.sons[0]
while result.kind in {tyGenericInst, tyGenericBody, tyRef, tyPtr, tyAlias}: while result.kind in {tyGenericInst, tyGenericBody, tyRef, tyPtr, tyAlias, tySink}:
result = lastSon(result) result = lastSon(result)
proc addInheritedFields(c: PContext, check: var IntSet, pos: var int, proc addInheritedFields(c: PContext, check: var IntSet, pos: var int,
@ -839,7 +836,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
result = liftingWalk(paramType.sons[0], true) result = liftingWalk(paramType.sons[0], true)
of tySequence, tySet, tyArray, tyOpenArray, of tySequence, tySet, tyArray, tyOpenArray,
tyVar, tyPtr, tyRef, tyProc: tyVar, tyLent, tyPtr, tyRef, tyProc:
# XXX: this is a bit strange, but proc(s: seq) # XXX: this is a bit strange, but proc(s: seq)
# produces tySequence(tyGenericParam, tyNone). # produces tySequence(tyGenericParam, tyNone).
# This also seems to be true when creating aliases # This also seems to be true when creating aliases
@ -995,7 +992,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if isType: localError(a.info, "':' expected") if isType: localError(a.info, "':' expected")
if kind in {skTemplate, skMacro}: if kind in {skTemplate, skMacro}:
typ = newTypeS(tyExpr, c) typ = newTypeS(tyExpr, c)
elif skipTypes(typ, {tyGenericInst, tyAlias}).kind == tyVoid: elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid:
continue continue
for j in countup(0, length-3): for j in countup(0, length-3):
var arg = newSymG(skParam, a.sons[j], c) var arg = newSymG(skParam, a.sons[j], c)
@ -1027,7 +1024,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if r != nil: if r != nil:
# turn explicit 'void' return type into 'nil' because the rest of the # turn explicit 'void' return type into 'nil' because the rest of the
# compiler only checks for 'nil': # compiler only checks for 'nil':
if skipTypes(r, {tyGenericInst, tyAlias}).kind != tyVoid: if skipTypes(r, {tyGenericInst, tyAlias, tySink}).kind != tyVoid:
# 'auto' as a return type does not imply a generic: # 'auto' as a return type does not imply a generic:
if r.kind == tyAnything: if r.kind == tyAnything:
# 'p(): auto' and 'p(): expr' are equivalent, but the rest of the # 'p(): auto' and 'p(): expr' are equivalent, but the rest of the
@ -1338,7 +1335,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = semRangeAux(c, n, prev) result = semRangeAux(c, n, prev)
elif n[0].kind == nkNilLit and n.len == 2: elif n[0].kind == nkNilLit and n.len == 2:
result = semTypeNode(c, n.sons[1], prev) result = semTypeNode(c, n.sons[1], prev)
if result.skipTypes({tyGenericInst, tyAlias}).kind in NilableTypes+GenericTypes: if result.skipTypes({tyGenericInst, tyAlias, tySink}).kind in NilableTypes+GenericTypes:
if tfNotNil in result.flags: if tfNotNil in result.flags:
result = freshType(result, prev) result = freshType(result, prev)
result.flags.excl(tfNotNil) result.flags.excl(tfNotNil)
@ -1366,7 +1363,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
case n.len case n.len
of 3: of 3:
result = semTypeNode(c, n.sons[1], prev) result = semTypeNode(c, n.sons[1], prev)
if result.skipTypes({tyGenericInst, tyAlias}).kind in NilableTypes+GenericTypes+{tyForward} and if result.skipTypes({tyGenericInst, tyAlias, tySink}).kind in NilableTypes+GenericTypes+{tyForward} and
n.sons[2].kind == nkNilLit: n.sons[2].kind == nkNilLit:
result = freshType(result, prev) result = freshType(result, prev)
result.flags.incl(tfNotNil) result.flags.incl(tfNotNil)
@ -1419,7 +1416,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of mVar: of mVar:
result = newOrPrevType(tyVar, prev, c) result = newOrPrevType(tyVar, prev, c)
var base = semTypeNode(c, n.sons[1], nil) var base = semTypeNode(c, n.sons[1], nil)
if base.kind == tyVar: if base.kind in {tyVar, tyLent}:
localError(n.info, errVarVarTypeNotAllowed) localError(n.info, errVarVarTypeNotAllowed)
base = base.sons[0] base = base.sons[0]
addSonSkipIntLit(result, base) addSonSkipIntLit(result, base)
@ -1620,6 +1617,12 @@ proc processMagicType(c: PContext, m: PSym) =
rawAddSon(m.typ, newTypeS(tyNone, c)) rawAddSon(m.typ, newTypeS(tyNone, c))
of mPNimrodNode: of mPNimrodNode:
incl m.typ.flags, tfTriggersCompileTime incl m.typ.flags, tfTriggersCompileTime
of mException: discard
of mBuiltinType:
case m.name.s
of "lent": setMagicType(m, tyLent, ptrSize)
of "sink": setMagicType(m, tySink, 0)
else: localError(m.info, errTypeExpected)
else: localError(m.info, errTypeExpected) else: localError(m.info, errTypeExpected)
proc semGenericConstraints(c: PContext, x: PType): PType = proc semGenericConstraints(c: PContext, x: PType): PType =

View file

@ -17,7 +17,7 @@ const
proc checkPartialConstructedType(info: TLineInfo, t: PType) = proc checkPartialConstructedType(info: TLineInfo, t: PType) =
if tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject: if tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
localError(info, errInvalidPragmaX, "acyclic") localError(info, errInvalidPragmaX, "acyclic")
elif t.kind == tyVar and t.sons[0].kind == tyVar: elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
localError(info, errVarVarTypeNotAllowed) localError(info, errVarVarTypeNotAllowed)
proc checkConstructedType*(info: TLineInfo, typ: PType) = proc checkConstructedType*(info: TLineInfo, typ: PType) =
@ -25,7 +25,7 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) =
if t.kind in tyTypeClasses: discard if t.kind in tyTypeClasses: discard
elif tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject: elif tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
localError(info, errInvalidPragmaX, "acyclic") localError(info, errInvalidPragmaX, "acyclic")
elif t.kind == tyVar and t.sons[0].kind == tyVar: elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
localError(info, errVarVarTypeNotAllowed) localError(info, errVarVarTypeNotAllowed)
elif computeSize(t) == szIllegalRecursion: elif computeSize(t) == szIllegalRecursion:
localError(info, errIllegalRecursionInTypeX, typeToString(t)) localError(info, errIllegalRecursionInTypeX, typeToString(t))
@ -518,7 +518,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
var r = replaceTypeVarsT(cl, result.sons[i]) var r = replaceTypeVarsT(cl, result.sons[i])
if result.kind == tyObject: if result.kind == tyObject:
# carefully coded to not skip the precious tyGenericInst: # carefully coded to not skip the precious tyGenericInst:
let r2 = r.skipTypes({tyAlias}) let r2 = r.skipTypes({tyAlias, tySink})
if r2.kind in {tyPtr, tyRef}: if r2.kind in {tyPtr, tyRef}:
r = skipTypes(r2, {tyPtr, tyRef}) r = skipTypes(r2, {tyPtr, tyRef})
result.sons[i] = r result.sons[i] = r

View file

@ -180,7 +180,8 @@ proc sumGeneric(t: PType): int =
while true: while true:
case t.kind case t.kind
of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct, of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct,
tyOpenArray, tyVarargs, tySet, tyRange, tySequence, tyGenericBody: tyOpenArray, tyVarargs, tySet, tyRange, tySequence, tyGenericBody,
tyLent:
t = t.lastSon t = t.lastSon
inc result inc result
of tyOr: of tyOr:
@ -207,7 +208,7 @@ proc sumGeneric(t: PType): int =
of tyStatic: of tyStatic:
return t.sons[0].sumGeneric + 1 return t.sons[0].sumGeneric + 1
of tyGenericParam, tyExpr, tyStmt: break of tyGenericParam, tyExpr, tyStmt: break
of tyAlias: t = t.lastSon of tyAlias, tySink: t = t.lastSon
of tyBool, tyChar, tyEnum, tyObject, tyPointer, of tyBool, tyChar, tyEnum, tyObject, tyPointer,
tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
tyUInt..tyUInt64, tyCompositeTypeClass: tyUInt..tyUInt64, tyCompositeTypeClass:
@ -464,7 +465,7 @@ proc skipToObject(t: PType; skipped: var SkippedPtr): PType =
inc ptrs inc ptrs
skipped = skippedPtr skipped = skippedPtr
r = r.lastSon r = r.lastSon
of tyGenericBody, tyGenericInst, tyAlias: of tyGenericBody, tyGenericInst, tyAlias, tySink:
r = r.lastSon r = r.lastSon
else: else:
break break
@ -524,7 +525,7 @@ proc allowsNil(f: PType): TTypeRelation {.inline.} =
result = if tfNotNil notin f.flags: isSubtype else: isNone result = if tfNotNil notin f.flags: isSubtype else: isNone
proc inconsistentVarTypes(f, a: PType): bool {.inline.} = proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar) result = f.kind != a.kind and (f.kind in {tyVar, tyLent} or a.kind in {tyVar, tyLent})
proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
## For example we have: ## For example we have:
@ -889,7 +890,7 @@ proc inferStaticsInRange(c: var TCandidate,
doInferStatic(lowerBound, upperBound.intVal + 1 - lengthOrd(concrete)) doInferStatic(lowerBound, upperBound.intVal + 1 - lengthOrd(concrete))
template subtypeCheck() = template subtypeCheck() =
if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar}: if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar, tyLent}:
result = isNone result = isNone
proc isCovariantPtr(c: var TCandidate, f, a: PType): bool = proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
@ -897,7 +898,7 @@ proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
assert f.kind == a.kind assert f.kind == a.kind
template baseTypesCheck(lhs, rhs: PType): bool = template baseTypesCheck(lhs, rhs: PType): bool =
lhs.kind notin {tyPtr, tyRef, tyVar} and lhs.kind notin {tyPtr, tyRef, tyVar, tyLent} and
typeRel(c, lhs, rhs, {trNoCovariance}) == isSubtype typeRel(c, lhs, rhs, {trNoCovariance}) == isSubtype
case f.kind case f.kind
@ -983,17 +984,17 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
template doBind: bool = trDontBind notin flags template doBind: bool = trDontBind notin flags
# var and static arguments match regular modifier-free types # var and static arguments match regular modifier-free types
var a = aOrig.skipTypes({tyStatic, tyVar}).maybeSkipDistinct(c.calleeSym) var a = aOrig.skipTypes({tyStatic, tyVar, tyLent}).maybeSkipDistinct(c.calleeSym)
# XXX: Theoretically, maybeSkipDistinct could be called before we even # XXX: Theoretically, maybeSkipDistinct could be called before we even
# start the param matching process. This could be done in `prepareOperand` # start the param matching process. This could be done in `prepareOperand`
# for example, but unfortunately `prepareOperand` is not called in certain # for example, but unfortunately `prepareOperand` is not called in certain
# situation when nkDotExpr are rotated to nkDotCalls # situation when nkDotExpr are rotated to nkDotCalls
if aOrig.kind == tyAlias: if aOrig.kind in {tyAlias, tySink}:
return typeRel(c, f, lastSon(aOrig)) return typeRel(c, f, lastSon(aOrig))
if a.kind == tyGenericInst and if a.kind == tyGenericInst and
skipTypes(f, {tyVar}).kind notin { skipTypes(f, {tyVar, tyLent}).kind notin {
tyGenericBody, tyGenericInvocation, tyGenericBody, tyGenericInvocation,
tyGenericInst, tyGenericParam} + tyTypeClasses: tyGenericInst, tyGenericParam} + tyTypeClasses:
return typeRel(c, f, lastSon(a)) return typeRel(c, f, lastSon(a))
@ -1105,8 +1106,8 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
of tyFloat32: result = handleFloatRange(f, a) of tyFloat32: result = handleFloatRange(f, a)
of tyFloat64: result = handleFloatRange(f, a) of tyFloat64: result = handleFloatRange(f, a)
of tyFloat128: result = handleFloatRange(f, a) of tyFloat128: result = handleFloatRange(f, a)
of tyVar: of tyVar, tyLent:
if aOrig.kind == tyVar: result = typeRel(c, f.base, aOrig.base) if aOrig.kind == f.kind: result = typeRel(c, f.base, aOrig.base)
else: result = typeRel(c, f.base, aOrig, flags + {trNoCovariance}) else: result = typeRel(c, f.base, aOrig, flags + {trNoCovariance})
subtypeCheck() subtypeCheck()
of tyArray: of tyArray:
@ -1311,7 +1312,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
of tyEmpty, tyVoid: of tyEmpty, tyVoid:
if a.kind == f.kind: result = isEqual if a.kind == f.kind: result = isEqual
of tyAlias: of tyAlias, tySink:
result = typeRel(c, lastSon(f), a) result = typeRel(c, lastSon(f), a)
of tyGenericInst: of tyGenericInst:
@ -1497,7 +1498,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
considerPreviousT: considerPreviousT:
let targetKind = f.sons[0].kind let targetKind = f.sons[0].kind
let effectiveArgType = a.skipTypes({tyRange, tyGenericInst, let effectiveArgType = a.skipTypes({tyRange, tyGenericInst,
tyBuiltInTypeClass, tyAlias}) tyBuiltInTypeClass, tyAlias, tySink})
let typeClassMatches = targetKind == effectiveArgType.kind and let typeClassMatches = targetKind == effectiveArgType.kind and
not effectiveArgType.isEmptyContainer not effectiveArgType.isEmptyContainer
if typeClassMatches or if typeClassMatches or
@ -2068,7 +2069,8 @@ proc prepareNamedParam(a: PNode) =
proc arrayConstr(c: PContext, n: PNode): PType = proc arrayConstr(c: PContext, n: PNode): PType =
result = newTypeS(tyArray, c) result = newTypeS(tyArray, c)
rawAddSon(result, makeRangeType(c, 0, 0, n.info)) rawAddSon(result, makeRangeType(c, 0, 0, n.info))
addSonSkipIntLit(result, skipTypes(n.typ, {tyGenericInst, tyVar, tyOrdinal})) addSonSkipIntLit(result, skipTypes(n.typ,
{tyGenericInst, tyVar, tyLent, tyOrdinal}))
proc arrayConstr(c: PContext, info: TLineInfo): PType = proc arrayConstr(c: PContext, info: TLineInfo): PType =
result = newTypeS(tyArray, c) result = newTypeS(tyArray, c)

View file

@ -240,7 +240,7 @@ proc suggestField(c: PContext, s: PSym; f: PNode; info: TLineInfo; outputs: var
proc getQuality(s: PSym): range[0..100] = proc getQuality(s: PSym): range[0..100] =
if s.typ != nil and s.typ.len > 1: if s.typ != nil and s.typ.len > 1:
var exp = s.typ.sons[1].skipTypes({tyGenericInst, tyVar, tyAlias}) var exp = s.typ.sons[1].skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
if exp.kind == tyVarargs: exp = elemType(exp) if exp.kind == tyVarargs: exp = elemType(exp)
if exp.kind in {tyExpr, tyStmt, tyGenericParam, tyAnything}: return 50 if exp.kind in {tyExpr, tyStmt, tyGenericParam, tyAnything}: return 50
return 100 return 100
@ -309,7 +309,7 @@ proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
let m = s.getModule() let m = s.getModule()
if m != nil and sfSystemModule in m.flags: if m != nil and sfSystemModule in m.flags:
if s.kind == skType: return if s.kind == skType: return
var exp = s.typ.sons[1].skipTypes({tyGenericInst, tyVar, tyAlias}) var exp = s.typ.sons[1].skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
if exp.kind == tyVarargs: exp = elemType(exp) if exp.kind == tyVarargs: exp = elemType(exp)
if exp.kind in {tyExpr, tyStmt, tyGenericParam, tyAnything}: return if exp.kind in {tyExpr, tyStmt, tyGenericParam, tyAnything}: return
result = sigmatch.argtypeMatches(c, s.typ.sons[1], firstArg) result = sigmatch.argtypeMatches(c, s.typ.sons[1], firstArg)
@ -378,8 +378,8 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
t = t.sons[0] t = t.sons[0]
suggestOperations(c, n, field, typ, outputs) suggestOperations(c, n, field, typ, outputs)
else: else:
let orig = typ # skipTypes(typ, {tyGenericInst, tyAlias}) let orig = typ # skipTypes(typ, {tyGenericInst, tyAlias, tySink})
typ = skipTypes(typ, {tyGenericInst, tyVar, tyPtr, tyRef, tyAlias}) typ = skipTypes(typ, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
if typ.kind == tyObject: if typ.kind == tyObject:
var t = typ var t = typ
while true: while true:

View file

@ -93,7 +93,7 @@ proc getCurrOwner(c: PTransf): PSym =
proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode = proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
let r = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info) let r = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info)
r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias}) r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias, tySink})
incl(r.flags, sfFromGeneric) incl(r.flags, sfFromGeneric)
let owner = getCurrOwner(c) let owner = getCurrOwner(c)
if owner.isIterator and not c.tooEarly: if owner.isIterator and not c.tooEarly:
@ -331,7 +331,7 @@ proc transformYield(c: PTransf, n: PNode): PTransNode =
# c.transCon.forStmt.len == 3 means that there is one for loop variable # c.transCon.forStmt.len == 3 means that there is one for loop variable
# and thus no tuple unpacking: # and thus no tuple unpacking:
if e.typ.isNil: return result # can happen in nimsuggest for unknown reasons if e.typ.isNil: return result # can happen in nimsuggest for unknown reasons
if skipTypes(e.typ, {tyGenericInst, tyAlias}).kind == tyTuple and if skipTypes(e.typ, {tyGenericInst, tyAlias, tySink}).kind == tyTuple and
c.transCon.forStmt.len != 3: c.transCon.forStmt.len != 3:
e = skipConv(e) e = skipConv(e)
if e.kind == nkPar: if e.kind == nkPar:
@ -506,7 +506,7 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
if putArgInto(arg.sons[i], formal) != paDirectMapping: return if putArgInto(arg.sons[i], formal) != paDirectMapping: return
result = paDirectMapping result = paDirectMapping
else: else:
if skipTypes(formal, abstractInst).kind == tyVar: result = paVarAsgn if skipTypes(formal, abstractInst).kind in {tyVar, tyLent}: result = paVarAsgn
else: result = paFastAsgn else: result = paFastAsgn
proc findWrongOwners(c: PTransf, n: PNode) = proc findWrongOwners(c: PTransf, n: PNode) =
@ -791,7 +791,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
case n.kind case n.kind
of nkSym: of nkSym:
result = transformSym(c, n) result = transformSym(c, n)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom:
# nothing to be done for leaves: # nothing to be done for leaves:
result = PTransNode(n) result = PTransNode(n)
of nkBracketExpr: result = transformArrayAccess(c, n) of nkBracketExpr: result = transformArrayAccess(c, n)
@ -914,7 +914,7 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
# Note: For interactive mode we cannot call 'passes.skipCodegen' and skip # Note: For interactive mode we cannot call 'passes.skipCodegen' and skip
# this step! We have to rely that the semantic pass transforms too errornous # this step! We have to rely that the semantic pass transforms too errornous
# nodes into an empty node. # nodes into an empty node.
if c.fromCache or nfTransf in n.flags: return n if c.rd != nil or nfTransf in n.flags: return n
pushTransCon(c, newTransCon(owner)) pushTransCon(c, newTransCon(owner))
result = PNode(transform(c, n)) result = PNode(transform(c, n))
popTransCon(c) popTransCon(c)

View file

@ -102,7 +102,7 @@ proc isDeepConstExpr*(n: PNode): bool =
if not isDeepConstExpr(n.sons[i]): return false if not isDeepConstExpr(n.sons[i]): return false
if n.typ.isNil: result = true if n.typ.isNil: result = true
else: else:
let t = n.typ.skipTypes({tyGenericInst, tyDistinct, tyAlias}) let t = n.typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink})
if t.kind in {tyRef, tyPtr}: return false if t.kind in {tyRef, tyPtr}: return false
if t.kind != tyObject or not isCaseObj(t.n): if t.kind != tyObject or not isCaseObj(t.n):
result = true result = true

View file

@ -51,17 +51,17 @@ const
# TODO: Remove tyTypeDesc from each abstractX and (where necessary) # TODO: Remove tyTypeDesc from each abstractX and (where necessary)
# replace with typedescX # replace with typedescX
abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal, abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal,
tyTypeDesc, tyAlias, tyInferred} tyTypeDesc, tyAlias, tyInferred, tySink, tyLent}
abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc,
tyAlias, tyInferred} tyAlias, tyInferred, tySink, tyLent}
abstractRange* = {tyGenericInst, tyRange, tyDistinct, tyOrdinal, tyTypeDesc, abstractRange* = {tyGenericInst, tyRange, tyDistinct, tyOrdinal, tyTypeDesc,
tyAlias, tyInferred} tyAlias, tyInferred, tySink}
abstractVarRange* = {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal, abstractVarRange* = {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal,
tyTypeDesc, tyAlias, tyInferred} tyTypeDesc, tyAlias, tyInferred, tySink}
abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias,
tyInferred} tyInferred, tySink}
skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyTypeDesc, tyAlias, skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyTypeDesc, tyAlias,
tyInferred} tyInferred, tySink, tyLent}
# typedescX is used if we're sure tyTypeDesc should be included (or skipped) # typedescX is used if we're sure tyTypeDesc should be included (or skipped)
typedescPtrs* = abstractPtrs + {tyTypeDesc} typedescPtrs* = abstractPtrs + {tyTypeDesc}
typedescInst* = abstractInst + {tyTypeDesc} typedescInst* = abstractInst + {tyTypeDesc}
@ -388,8 +388,8 @@ const
"int", "int8", "int16", "int32", "int64", "int", "int8", "int16", "int32", "int64",
"float", "float32", "float64", "float128", "float", "float32", "float64", "float128",
"uint", "uint8", "uint16", "uint32", "uint64", "uint", "uint8", "uint16", "uint32", "uint64",
"unused0", "unused1", "opt", "sink",
"unused2", "varargs[$1]", "unused", "Error Type", "lent", "varargs[$1]", "unused", "Error Type",
"BuiltInTypeClass", "UserTypeClass", "BuiltInTypeClass", "UserTypeClass",
"UserTypeClassInst", "CompositeTypeClass", "inferred", "UserTypeClassInst", "CompositeTypeClass", "inferred",
"and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor", "and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor",
@ -539,7 +539,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
add(result, typeToString(t.sons[i])) add(result, typeToString(t.sons[i]))
if i < sonsLen(t) - 1: add(result, ", ") if i < sonsLen(t) - 1: add(result, ", ")
add(result, ')') add(result, ')')
of tyPtr, tyRef, tyVar: of tyPtr, tyRef, tyVar, tyLent:
result = typeToStr[t.kind] result = typeToStr[t.kind]
if t.len >= 2: if t.len >= 2:
setLen(result, result.len-1) setLen(result, result.len-1)
@ -581,6 +581,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
if len(prag) != 0: add(result, "{." & prag & ".}") if len(prag) != 0: add(result, "{." & prag & ".}")
of tyVarargs: of tyVarargs:
result = typeToStr[t.kind] % typeToString(t.sons[0]) result = typeToStr[t.kind] % typeToString(t.sons[0])
of tySink:
result = "sink " & typeToString(t.sons[0])
else: else:
result = typeToStr[t.kind] result = typeToStr[t.kind]
result.addTypeFlags(t) result.addTypeFlags(t)
@ -968,7 +970,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
if result and ExactGenericParams in c.flags: if result and ExactGenericParams in c.flags:
result = a.sym.position == b.sym.position result = a.sym.position == b.sym.position
of tyGenericInvocation, tyGenericBody, tySequence, of tyGenericInvocation, tyGenericBody, tySequence,
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyLent, tySink,
tyArray, tyProc, tyVarargs, tyOrdinal, tyTypeClasses, tyOpt: tyArray, tyProc, tyVarargs, tyOrdinal, tyTypeClasses, tyOpt:
cycleCheck() cycleCheck()
if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n
@ -992,7 +994,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
cycleCheck() cycleCheck()
result = sameTypeAux(a.lastSon, b.lastSon, c) result = sameTypeAux(a.lastSon, b.lastSon, c)
of tyNone: result = false of tyNone: result = false
of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("sameFlags") of tyUnused, tyOptAsRef: internalError("sameFlags")
proc sameBackendType*(x, y: PType): bool = proc sameBackendType*(x, y: PType): bool =
var c = initSameTypeClosure() var c = initSameTypeClosure()
@ -1101,11 +1103,11 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
if containsOrIncl(marker, typ.id): return if containsOrIncl(marker, typ.id): return
var t = skipTypes(typ, abstractInst-{tyTypeDesc}) var t = skipTypes(typ, abstractInst-{tyTypeDesc})
case t.kind case t.kind
of tyVar: of tyVar, tyLent:
if kind in {skProc, skFunc, skConst}: return t if kind in {skProc, skFunc, skConst}: return t
var t2 = skipTypes(t.sons[0], abstractInst-{tyTypeDesc}) var t2 = skipTypes(t.sons[0], abstractInst-{tyTypeDesc})
case t2.kind case t2.kind
of tyVar: of tyVar, tyLent:
if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap
of tyOpenArray: of tyOpenArray:
if kind != skParam: result = t if kind != skParam: result = t
@ -1143,7 +1145,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
of tyRange: of tyRange:
if skipTypes(t.sons[0], abstractInst-{tyTypeDesc}).kind notin if skipTypes(t.sons[0], abstractInst-{tyTypeDesc}).kind notin
{tyChar, tyEnum, tyInt..tyFloat128, tyUInt8..tyUInt32}: result = t {tyChar, tyEnum, tyInt..tyFloat128, tyUInt8..tyUInt32}: result = t
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs, tySink:
if kind != skParam: result = t if kind != skParam: result = t
else: result = typeAllowedAux(marker, t.sons[0], skVar, flags) else: result = typeAllowedAux(marker, t.sons[0], skVar, flags)
of tySequence, tyOpt: of tySequence, tyOpt:
@ -1174,7 +1176,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
# for now same as error node; we say it's a valid type as it should # for now same as error node; we say it's a valid type as it should
# prevent cascading errors: # prevent cascading errors:
result = nil result = nil
of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("typeAllowedAux") of tyUnused, tyOptAsRef: internalError("typeAllowedAux")
proc typeAllowed*(t: PType, kind: TSymKind): PType = proc typeAllowed*(t: PType, kind: TSymKind): PType =
# returns 'nil' on success and otherwise the part of the type that is # returns 'nil' on success and otherwise the part of the type that is
@ -1322,7 +1324,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
if typ.callConv == ccClosure: result = 2 * ptrSize if typ.callConv == ccClosure: result = 2 * ptrSize
else: result = ptrSize else: result = ptrSize
a = ptrSize a = ptrSize
of tyNil, tyCString, tyString, tySequence, tyPtr, tyRef, tyVar, tyOpenArray: of tyNil, tyCString, tyString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray:
let base = typ.lastSon let base = typ.lastSon
if base == typ or (base.kind == tyTuple and base.size==szIllegalRecursion): if base == typ or (base.kind == tyTuple and base.size==szIllegalRecursion):
result = szIllegalRecursion result = szIllegalRecursion

View file

@ -209,6 +209,8 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
else: else:
result = mapTypeToBracket("ref", mRef, t, info) result = mapTypeToBracket("ref", mRef, t, info)
of tyVar: result = mapTypeToBracket("var", mVar, t, info) of tyVar: result = mapTypeToBracket("var", mVar, t, info)
of tyLent: result = mapTypeToBracket("lent", mBuiltinType, t, info)
of tySink: result = mapTypeToBracket("sink", mBuiltinType, t, info)
of tySequence: result = mapTypeToBracket("seq", mSeq, t, info) of tySequence: result = mapTypeToBracket("seq", mSeq, t, info)
of tyOpt: result = mapTypeToBracket("opt", mOpt, t, info) of tyOpt: result = mapTypeToBracket("opt", mOpt, t, info)
of tyProc: of tyProc:
@ -274,7 +276,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
result.add atomicType("static", mNone) result.add atomicType("static", mNone)
if t.n != nil: if t.n != nil:
result.add t.n.copyTree result.add t.n.copyTree
of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("mapTypeToAstX") of tyUnused, tyOptAsRef: internalError("mapTypeToAstX")
proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode = proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode =
result = mapTypeToAstX(t, info, false, true) result = mapTypeToAstX(t, info, false, true)

View file

@ -30,7 +30,7 @@
import import
strutils, ast, astalgo, types, msgs, renderer, vmdef, strutils, ast, astalgo, types, msgs, renderer, vmdef,
trees, intsets, rodread, magicsys, options, lowerings trees, intsets, rodread, magicsys, options, lowerings
import platform
from os import splitFile from os import splitFile
when hasFFI: when hasFFI:
@ -761,6 +761,49 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) =
c.gABC(n, opcCard, dest, tmp) c.gABC(n, opcCard, dest, tmp)
c.freeTemp(tmp) c.freeTemp(tmp)
proc genIntCast(c: PCtx; n: PNode; dest: var TDest) =
const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar}
var signedIntegers = {tyInt8..tyInt32}
var unsignedIntegers = {tyUInt8..tyUInt32, tyChar}
let src = n.sons[1].typ.skipTypes(abstractRange)#.kind
let dst = n.sons[0].typ.skipTypes(abstractRange)#.kind
let src_size = src.getSize
if platform.intSize < 8:
signedIntegers.incl(tyInt)
unsignedIntegers.incl(tyUInt)
if src_size == dst.getSize and src.kind in allowedIntegers and
dst.kind in allowedIntegers:
let tmp = c.genx(n.sons[1])
var tmp2 = c.getTemp(n.sons[1].typ)
let tmp3 = c.getTemp(n.sons[1].typ)
if dest < 0: dest = c.getTemp(n[0].typ)
proc mkIntLit(ival: int): int =
result = genLiteral(c, newIntTypeNode(nkIntLit, ival, getSysType(tyInt)))
if src.kind in unsignedIntegers and dst.kind in signedIntegers:
# cast unsigned to signed integer of same size
# signedVal = (unsignedVal xor offset) -% offset
let offset = 1 shl (src_size * 8 - 1)
c.gABx(n, opcLdConst, tmp2, mkIntLit(offset))
c.gABC(n, opcBitxorInt, tmp3, tmp, tmp2)
c.gABC(n, opcSubInt, dest, tmp3, tmp2)
elif src.kind in signedIntegers and dst.kind in unsignedIntegers:
# cast signed to unsigned integer of same size
# unsignedVal = (offset +% signedVal +% 1) and offset
let offset = (1 shl (src_size * 8)) - 1
c.gABx(n, opcLdConst, tmp2, mkIntLit(offset))
c.gABx(n, opcLdConst, dest, mkIntLit(offset+1))
c.gABC(n, opcAddu, tmp3, tmp, dest)
c.gABC(n, opcNarrowU, tmp3, TRegister(src_size*8))
c.gABC(n, opcBitandInt, dest, tmp3, tmp2)
else:
c.gABC(n, opcAsgnInt, dest, tmp)
c.freeTemp(tmp)
c.freeTemp(tmp2)
c.freeTemp(tmp3)
else:
globalError(n.info, errGenerated, "VM is only allowed to 'cast' between integers of same size")
proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
case m case m
of mAnd: c.genAndOr(n, opcFJmp, dest) of mAnd: c.genAndOr(n, opcFJmp, dest)
@ -1245,7 +1288,7 @@ proc whichAsgnOpc(n: PNode): TOpcode =
opcAsgnStr opcAsgnStr
of tyFloat..tyFloat128: of tyFloat..tyFloat128:
opcAsgnFloat opcAsgnFloat
of tyRef, tyNil, tyVar: of tyRef, tyNil, tyVar, tyLent:
opcAsgnRef opcAsgnRef
else: else:
opcAsgnComplex opcAsgnComplex
@ -1438,7 +1481,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
cannotEval(n) cannotEval(n)
template needsRegLoad(): untyped = template needsRegLoad(): untyped =
gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar})) gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar, tyLent}))
proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
flags: TGenFlags) = flags: TGenFlags) =
@ -1510,7 +1553,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
result = newNodeIT(nkFloatLit, info, t) result = newNodeIT(nkFloatLit, info, t)
of tyCString, tyString: of tyCString, tyString:
result = newNodeIT(nkStrLit, info, t) result = newNodeIT(nkStrLit, info, t)
of tyVar, tyPointer, tyPtr, tySequence, tyExpr, of tyVar, tyLent, tyPointer, tyPtr, tySequence, tyExpr,
tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil: tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil:
result = newNodeIT(nkNilLit, info, t) result = newNodeIT(nkNilLit, info, t)
of tyProc: of tyProc:
@ -1844,9 +1887,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
if allowCast in c.features: if allowCast in c.features:
genConv(c, n, n.sons[1], dest, opcCast) genConv(c, n, n.sons[1], dest, opcCast)
else: else:
globalError(n.info, errGenerated, "VM is not allowed to 'cast'") genIntCast(c, n, dest)
of nkTypeOfExpr: of nkTypeOfExpr:
genTypeLit(c, n.typ, dest) genTypeLit(c, n.typ, dest)
of nkComesFrom:
discard "XXX to implement for better stack traces"
else: else:
globalError(n.info, errGenerated, "cannot generate VM code for " & $n) globalError(n.info, errGenerated, "cannot generate VM code for " & $n)

View file

@ -102,7 +102,7 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet) =
else: else:
storeAny(s, t.lastSon, a[i], stored) storeAny(s, t.lastSon, a[i], stored)
s.add("]") s.add("]")
of tyRange, tyGenericInst, tyAlias: storeAny(s, t.lastSon, a, stored) of tyRange, tyGenericInst, tyAlias, tySink: storeAny(s, t.lastSon, a, stored)
of tyEnum: of tyEnum:
# we need a slow linear search because of enums with holes: # we need a slow linear search because of enums with holes:
for e in items(t.n): for e in items(t.n):
@ -275,7 +275,7 @@ proc loadAny(p: var JsonParser, t: PType,
next(p) next(p)
return return
raiseParseErr(p, "float expected") raiseParseErr(p, "float expected")
of tyRange, tyGenericInst, tyAlias: result = loadAny(p, t.lastSon, tab) of tyRange, tyGenericInst, tyAlias, tySink: result = loadAny(p, t.lastSon, tab)
else: else:
internalError "cannot marshal at compile-time " & t.typeToString internalError "cannot marshal at compile-time " & t.typeToString

View file

@ -45,7 +45,7 @@ type
wImportc, wExportc, wExportNims, wIncompleteStruct, wRequiresInit, wImportc, wExportc, wExportNims, wIncompleteStruct, wRequiresInit,
wAlign, wNodecl, wPure, wSideeffect, wHeader, wAlign, wNodecl, wPure, wSideeffect, wHeader,
wNosideeffect, wGcSafe, wNoreturn, wMerge, wLib, wDynlib, wNosideeffect, wGcSafe, wNoreturn, wMerge, wLib, wDynlib,
wCompilerproc, wProcVar, wBase, wUsed, wCompilerproc, wCore, wProcVar, wBase, wUsed,
wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef, wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef,
wLinedir, wStacktrace, wLinetrace, wLink, wCompile, wLinedir, wStacktrace, wLinetrace, wLink, wCompile,
wLinksys, wDeprecated, wVarargs, wCallconv, wBreakpoint, wDebugger, wLinksys, wDeprecated, wVarargs, wCallconv, wBreakpoint, wDebugger,
@ -131,7 +131,7 @@ const
"incompletestruct", "incompletestruct",
"requiresinit", "align", "nodecl", "pure", "sideeffect", "requiresinit", "align", "nodecl", "pure", "sideeffect",
"header", "nosideeffect", "gcsafe", "noreturn", "merge", "lib", "dynlib", "header", "nosideeffect", "gcsafe", "noreturn", "merge", "lib", "dynlib",
"compilerproc", "procvar", "base", "used", "compilerproc", "core", "procvar", "base", "used",
"fatal", "error", "warning", "hint", "line", "fatal", "error", "warning", "hint", "line",
"push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace", "push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace",
"link", "compile", "linksys", "deprecated", "varargs", "link", "compile", "linksys", "deprecated", "varargs",

View file

@ -59,6 +59,9 @@ path="$lib/pure"
debugger:off debugger:off
line_dir:off line_dir:off
dead_code_elim:on dead_code_elim:on
@if nimHasNilChecks:
nilchecks:off
@end
@end @end
@if release: @if release:

View file

@ -37,6 +37,7 @@ Advanced options:
--noMain do not generate a main procedure --noMain do not generate a main procedure
--genScript generate a compile script (in the 'nimcache' --genScript generate a compile script (in the 'nimcache'
subdirectory named 'compile_$project$scriptext') subdirectory named 'compile_$project$scriptext')
--genDeps generate a '.deps' file containing the dependencies
--os:SYMBOL set the target operating system (cross-compilation) --os:SYMBOL set the target operating system (cross-compilation)
--cpu:SYMBOL set the target processor (cross-compilation) --cpu:SYMBOL set the target processor (cross-compilation)
--debuginfo enables debug information --debuginfo enables debug information

View file

@ -918,7 +918,7 @@ This is equivalent to ``var``, but with ``nnkLetSection`` rather than
Concrete syntax: Concrete syntax:
.. code-block:: nim .. code-block:: nim
let v = 3 let a = 3
AST: AST:

View file

@ -92,6 +92,10 @@ Collections and algorithms
* `sequtils <sequtils.html>`_ * `sequtils <sequtils.html>`_
This module implements operations for the built-in seq type This module implements operations for the built-in seq type
which were inspired by functional programming languages. which were inspired by functional programming languages.
* `sharedtables <sharedtables.html>`_
Nim shared hash table support. Contains shared tables.
* `sharedlist <sharedlist.html>`_
Nim shared linked list support. Contains shared singly linked list.
String handling String handling
@ -102,6 +106,10 @@ String handling
case of a string, splitting a string into substrings, searching for case of a string, splitting a string into substrings, searching for
substrings, replacing substrings. substrings, replacing substrings.
* `strformat <strformat.html>`_
Macro based standard string interpolation / formatting. Inpired by
Python's ```f``-strings.
* `strmisc <strmisc.html>`_ * `strmisc <strmisc.html>`_
This module contains uncommon string handling operations that do not This module contains uncommon string handling operations that do not
fit with the commonly used operations in strutils. fit with the commonly used operations in strutils.
@ -379,6 +387,7 @@ Cryptography and Hashing
* `securehash <securehash.html>`_ * `securehash <securehash.html>`_
This module implements a sha1 encoder and decoder. This module implements a sha1 encoder and decoder.
Multimedia support Multimedia support
------------------ ------------------
@ -431,6 +440,8 @@ Modules for JS backend
* `jsffi <jsffi.html>`_ * `jsffi <jsffi.html>`_
Types and macros for easier interaction with JavaScript. Types and macros for easier interaction with JavaScript.
* `asyncjs <asyncjs.html>`_
Types and macros for writing asynchronous procedures in JavaScript.
Deprecated modules Deprecated modules
------------------ ------------------

View file

@ -1087,3 +1087,70 @@ In the above example, providing the -d flag causes the symbol
``FooBar`` to be overwritten at compile time, printing out 42. If the ``FooBar`` to be overwritten at compile time, printing out 42. If the
``-d:FooBar=42`` were to be omitted, the default value of 5 would be ``-d:FooBar=42`` were to be omitted, the default value of 5 would be
used. used.
Custom annotations
------------------
It is possible to define custom typed pragmas. Custom pragmas do not effect
code generation directly, but their presence can be detected by macros.
Custom pragmas are defined using templates annotated with pragma ``pragma``:
.. code-block:: nim
template dbTable(name: string, table_space: string = nil) {.pragma.}
template dbKey(name: string = nil, primary_key: bool = false) {.pragma.}
template dbForeignKey(t: typedesc) {.pragma.}
template dbIgnore {.pragma.}
Consider stylized example of possible Object Relation Mapping (ORM) implementation:
.. code-block:: nim
const tblspace {.strdefine.} = "dev" # switch for dev, test and prod environments
type
User {.dbTable("users", tblspace).} = object
id {.dbKey(primary_key = true).}: int
name {.dbKey"full_name".}: string
is_cached {.dbIgnore.}: bool
age: int
UserProfile {.dbTable("profiles", tblspace).} = object
id {.dbKey(primary_key = true).}: int
user_id {.dbForeignKey: User.}: int
read_access: bool
write_access: bool
admin_acess: bool
In this example custom pragmas are used to describe how Nim objects are
mapped to the schema of the relational database. Custom pragmas can have
zero or more arguments. In order to pass multiple arguments use one of
template call syntaxes. All arguments are typed and follow standard
overload resolution rules for templates. Therefore, it is possible to have
default values for arguments, pass by name, varargs, etc.
Custom pragmas can be used in all locations where ordinary pragmas can be
specified. It is possible to annotate procs, templates, type and variable
definitions, statements, etc.
Macros module includes helpers which can be used to simplify custom pragma
access `hasCustomPragma`, `getCustomPragmaVal`. Please consult macros module
documentation for details. These macros are no magic, they don't do anything
you cannot do yourself by walking AST object representation.
More examples with custom pragmas:
- Better serialization/deserialization control:
.. code-block:: nim
type MyObj = object
a {.dontSerialize.}: int
b {.defaultDeserialize: 5.}: int
c {.serializationKey: "_c".}: string
- Adopting type for gui inspector in a game engine:
.. code-block:: nim
type MyComponent = object
position {.editable, animatable.}: Vector3
alpha {.editRange: [0.0..1.0], animatable.}: float32

View file

@ -296,6 +296,10 @@ empty ``discard`` statement should be used.
For non ordinal types it is not possible to list every possible value and so For non ordinal types it is not possible to list every possible value and so
these always require an ``else`` part. these always require an ``else`` part.
As case statements perform compile-time exhaustiveness checks, the value in
every ``of`` branch must be known at compile time. This fact is also exploited
to generate more performant code.
As a special semantic extension, an expression in an ``of`` branch of a case As a special semantic extension, an expression in an ``of`` branch of a case
statement may evaluate to a set or array constructor; the set or array is then statement may evaluate to a set or array constructor; the set or array is then
expanded into a list of its elements: expanded into a list of its elements:

View file

@ -41,7 +41,8 @@ These integer types are pre-defined:
``int`` ``int``
the generic signed integer type; its size is platform dependent and has the the generic signed integer type; its size is platform dependent and has the
same size as a pointer. This type should be used in general. An integer same size as a pointer. This type should be used in general. An integer
literal that has no type suffix is of this type. literal that has no type suffix is of this type if it is in the range
``low(int32)..high(int32)`` otherwise the literal's type is ``int64``.
intXX intXX
additional signed integer types of XX bits use this naming scheme additional signed integer types of XX bits use this naming scheme

View file

@ -41,7 +41,7 @@ Save this code to the file "greetings.nim". Now compile and run it::
nim compile --run greetings.nim nim compile --run greetings.nim
With the ``--run`` `switch <nimc.html#command-line-switches>`_ Nim With the ``--run`` `switch <nimc.html#compiler-usage-command-line-switches>`_ Nim
executes the file automatically after compilation. You can give your program executes the file automatically after compilation. You can give your program
command line arguments by appending them after the filename:: command line arguments by appending them after the filename::
@ -58,7 +58,7 @@ To compile a release version use::
By default the Nim compiler generates a large amount of runtime checks By default the Nim compiler generates a large amount of runtime checks
aiming for your debugging pleasure. With ``-d:release`` these checks are aiming for your debugging pleasure. With ``-d:release`` these checks are
`turned off and optimizations are turned on `turned off and optimizations are turned on
<nimc.html#compile-time-symbols>`_. <nimc.html#compiler-usage-compile-time-symbols>`_.
Though it should be pretty obvious what the program does, I will explain the Though it should be pretty obvious what the program does, I will explain the
syntax: statements which are not indented are executed when the program syntax: statements which are not indented are executed when the program

View file

@ -97,7 +97,7 @@ proc exec(cmd: string, errorcode: int = QuitFailure, additionalPath = "") =
if not absolute.isAbsolute: if not absolute.isAbsolute:
absolute = getCurrentDir() / absolute absolute = getCurrentDir() / absolute
echo("Adding to $PATH: ", absolute) echo("Adding to $PATH: ", absolute)
putEnv("PATH", prevPath & PathSep & absolute) putEnv("PATH", (if prevPath.len > 0: prevPath & PathSep else: "") & absolute)
echo(cmd) echo(cmd)
if execShellCmd(cmd) != 0: quit("FAILURE", errorcode) if execShellCmd(cmd) != 0: quit("FAILURE", errorcode)
putEnv("PATH", prevPath) putEnv("PATH", prevPath)
@ -260,7 +260,7 @@ proc buildTools(latest: bool) =
" nimsuggest/nimsuggest.nim" " nimsuggest/nimsuggest.nim"
let nimgrepExe = "bin/nimgrep".exe let nimgrepExe = "bin/nimgrep".exe
nimexec "c -o:" & nimgrepExe & " tools/nimgrep.nim" nimexec "c -d:release -o:" & nimgrepExe & " tools/nimgrep.nim"
when defined(windows): buildVccTool() when defined(windows): buildVccTool()
#nimexec "c -o:" & ("bin/nimresolve".exe) & " tools/nimresolve.nim" #nimexec "c -o:" & ("bin/nimresolve".exe) & " tools/nimresolve.nim"
@ -402,7 +402,7 @@ proc winReleaseArch(arch: string) =
template withMingw(path, body) = template withMingw(path, body) =
let prevPath = getEnv("PATH") let prevPath = getEnv("PATH")
putEnv("PATH", path & PathSep & prevPath) putEnv("PATH", (if path.len > 0: path & PathSep else: "") & prevPath)
try: try:
body body
finally: finally:
@ -441,7 +441,7 @@ template `|`(a, b): string = (if a.len > 0: a else: b)
proc tests(args: string) = proc tests(args: string) =
# we compile the tester with taintMode:on to have a basic # we compile the tester with taintMode:on to have a basic
# taint mode test :-) # taint mode test :-)
nimexec "cc --taintMode:on tests/testament/tester" nimexec "cc --taintMode:on --opt:speed tests/testament/tester"
# Since tests take a long time (on my machine), and we want to defy Murhpys # Since tests take a long time (on my machine), and we want to defy Murhpys
# law - lets make sure the compiler really is freshly compiled! # law - lets make sure the compiler really is freshly compiled!
nimexec "c --lib:lib -d:release --opt:speed compiler/nim.nim" nimexec "c --lib:lib -d:release --opt:speed compiler/nim.nim"

35
lib/core/allocators.nim Normal file
View file

@ -0,0 +1,35 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2017 Nim contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
type
Allocator* = ptr object {.inheritable.}
alloc*: proc (a: Allocator; size: int; alignment: int = 8): pointer {.nimcall.}
dealloc*: proc (a: Allocator; p: pointer; size: int) {.nimcall.}
realloc*: proc (a: Allocator; p: pointer; oldSize, newSize: int): pointer {.nimcall.}
var
currentAllocator {.threadvar.}: Allocator
proc getCurrentAllocator*(): Allocator =
result = currentAllocator
proc setCurrentAllocator*(a: Allocator) =
currentAllocator = a
proc alloc*(size: int; alignment: int = 8): pointer =
let a = getCurrentAllocator()
result = a.alloc(a, size, alignment)
proc dealloc*(p: pointer; size: int) =
let a = getCurrentAllocator()
a.dealloc(a, p, size)
proc realloc*(p: pointer; oldSize, newSize: int): pointer =
let a = getCurrentAllocator()
result = a.realloc(a, p, oldSize, newSize)

View file

@ -21,7 +21,7 @@ type
nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkUIntLit, nnkUInt8Lit, nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkUIntLit, nnkUInt8Lit,
nnkUInt16Lit, nnkUInt32Lit, nnkUInt64Lit, nnkFloatLit, nnkUInt16Lit, nnkUInt32Lit, nnkUInt64Lit, nnkFloatLit,
nnkFloat32Lit, nnkFloat64Lit, nnkFloat128Lit, nnkStrLit, nnkRStrLit, nnkFloat32Lit, nnkFloat64Lit, nnkFloat128Lit, nnkStrLit, nnkRStrLit,
nnkTripleStrLit, nnkNilLit, nnkMetaNode, nnkDotCall, nnkTripleStrLit, nnkNilLit, nnkComesFrom, nnkDotCall,
nnkCommand, nnkCall, nnkCallStrLit, nnkInfix, nnkCommand, nnkCall, nnkCallStrLit, nnkInfix,
nnkPrefix, nnkPostfix, nnkHiddenCallConv, nnkPrefix, nnkPostfix, nnkHiddenCallConv,
nnkExprEqExpr, nnkExprEqExpr,
@ -130,6 +130,7 @@ const
nnkLiterals* = {nnkCharLit..nnkNilLit} nnkLiterals* = {nnkCharLit..nnkNilLit}
nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand, nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
nnkCallStrLit} nnkCallStrLit}
nnkPragmaCallKinds = {nnkExprColonExpr, nnkCall, nnkCallStrLit}
proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.} proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.}
## constructs an identifier from the string `s` ## constructs an identifier from the string `s`
@ -1213,6 +1214,59 @@ macro expandMacros*(body: typed): untyped =
result = getAst(inner(body)) result = getAst(inner(body))
echo result.toStrLit echo result.toStrLit
proc customPragmaNode(n: NimNode): NimNode =
expectKind(n, {nnkSym, nnkDotExpr})
if n.kind == nnkSym:
let sym = n.symbol.getImpl()
sym.expectRoutine()
result = sym.pragma
elif n.kind == nnkDotExpr:
let typDef = getImpl(getTypeInst(n[0]).symbol)
typDef.expectKind(nnkTypeDef)
typDef[2].expectKind(nnkObjectTy)
let recList = typDef[2][2]
for identDefs in recList:
for i in 0 .. identDefs.len - 3:
if identDefs[i].kind == nnkPragmaExpr and
identDefs[i][0].kind == nnkIdent and $identDefs[i][0] == $n[1]:
return identDefs[i][1]
macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped =
## Expands to `true` if expression `n` which is expected to be `nnkDotExpr`
## has custom pragma `cp`.
##
## .. code-block:: nim
## template myAttr() {.pragma.}
## type
## MyObj = object
## myField {.myAttr.}: int
## var o: MyObj
## assert(o.myField.hasCustomPragma(myAttr) == 0)
let pragmaNode = customPragmaNode(n)
for p in pragmaNode:
if (p.kind == nnkSym and p == cp) or
(p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp):
return newLit(true)
return newLit(false)
macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped =
## Expands to value of custom pragma `cp` of expression `n` which is expected
## to be `nnkDotExpr`.
##
## .. code-block:: nim
## template serializationKey(key: string) {.pragma.}
## type
## MyObj = object
## myField {.serializationKey: "mf".}: int
## var o: MyObj
## assert(o.myField.getCustomPragmaVal(serializationKey) == "mf")
let pragmaNode = customPragmaNode(n)
for p in pragmaNode:
if p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp:
return p[1]
return newEmptyNode()
when not defined(booting): when not defined(booting):
template emit*(e: static[string]): untyped {.deprecated.} = template emit*(e: static[string]): untyped {.deprecated.} =
## accepts a single string argument and treats it as nim code ## accepts a single string argument and treats it as nim code

97
lib/core/refs.nim Normal file
View file

@ -0,0 +1,97 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2017 Nim contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Default ref implementation used by Nim's core.
# We cannot use the allocator interface here as we require a heap walker to
# exist. Thus we import 'alloc' directly here to get our own heap that is
# all under the GC's control and can use the ``allObjects`` iterator which
# is crucial for the "sweep" phase.
import typelayouts, alloc
type
TracingGc = ptr object of Allocator
visit*: proc (fieldAddr: ptr pointer; a: Allocator) {.nimcall.}
GcColor = enum
white = 0, black = 1, grey = 2 ## to flip the meaning of white/black
## perform (1 - col)
GcHeader = object
t: ptr TypeLayout
color: GcColor
Cell = ptr GcHeader
GcFrame {.core.} = object
prev: ptr GcFrame
marker: proc (self: GcFrame; a: Allocator)
Phase = enum
None, Marking, Sweeping
GcHeap = object
r: MemRegion
phase: Phase
currBlack, currWhite: GcColor
greyStack: seq[Cell]
var
gch {.threadvar.}: GcHeap
proc `=trace`[T](a: ref T) =
if not marked(a):
mark(a)
`=trace`(a[])
template usrToCell(p: pointer): Cell =
template cellToUsr(cell: Cell): pointer =
cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(GcHeader)))
template usrToCell(usr: pointer): Cell =
cast[Cell](cast[ByteAddress](usr)-%ByteAddress(sizeof(GcHeader)))
template markGrey(x: Cell) =
if x.color == gch.currWhite and phase == Marking:
x.color = grey
add(gch.greyStack, x)
proc `=`[T](dest: var ref T; src: ref T) =
## full write barrier implementation.
if src != nil:
let s = usrToCell(src)
markGrey(s)
system.`=`(dest, src)
proc linkGcFrame(f: ptr GcFrame) {.core.}
proc unlinkGcFrame() {.core.}
proc setGcFrame(f: ptr GcFrame) {.core.}
proc registerGlobal(p: pointer; t: ptr TypeLayout) {.core.}
proc unregisterGlobal(p: pointer; t: ptr TypeLayout) {.core.}
proc registerThreadvar(p: pointer; t: ptr TypeLayout) {.core.}
proc unregisterThreadvar(p: pointer; t: ptr TypeLayout) {.core.}
proc newImpl(t: ptr TypeLayout): pointer =
let r = cast[Cell](rawAlloc(t.size + sizeof(GcHeader)))
r.typ = t
result = r +! sizeof(GcHeader)
template new*[T](x: var ref T) =
x = newImpl(getTypeLayout(x))
when false:
# implement these if your GC requires them:
proc writeBarrierLocal() {.core.}
proc writeBarrierGlobal() {.core.}
proc writeBarrierGeneric() {.core.}

139
lib/core/seqs.nim Normal file
View file

@ -0,0 +1,139 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2017 Nim contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import allocators, typetraits
## Default seq implementation used by Nim's core.
type
seq*[T] = object
len, cap: int
data: ptr UncheckedArray[T]
template frees(s) = dealloc(s.data, s.cap * sizeof(T))
# XXX make code memory safe for overflows in '*'
proc nimSeqLiteral[T](x: openArray[T]): seq[T] {.core.} =
seq[T](len: x.len, cap: x.len, data: x)
when defined(nimHasTrace):
proc `=trace`[T](s: seq[T]; a: Allocator) =
for i in 0 ..< s.len: `=trace`(s.data[i], a)
proc `=destroy`[T](x: var seq[T]) =
if x.data != nil:
when not supportsCopyMem(T):
for i in 0..<x.len: `=destroy`(x[i])
frees(x)
x.data = nil
x.len = 0
x.cap = 0
proc `=`[T](a: var seq[T]; b: seq[T]) =
if a.data == b.data: return
if a.data != nil:
frees(a)
a.data = nil
a.len = b.len
a.cap = b.cap
if b.data != nil:
a.data = cast[type(a.data)](alloc(a.cap * sizeof(T)))
when supportsCopyMem(T):
copyMem(a.data, b.data, a.cap * sizeof(T))
else:
for i in 0..<a.len:
a.data[i] = b.data[i]
proc `=sink`[T](a: var seq[T]; b: seq[T]) =
if a.data != nil and a.data != b.data:
frees(a)
a.len = b.len
a.cap = b.cap
a.data = b.data
proc resize[T](s: var seq[T]) =
let old = s.cap
if old == 0: s.cap = 8
else: s.cap = (s.cap * 3) shr 1
s.data = cast[type(s.data)](realloc(s.data, old * sizeof(T), s.cap * sizeof(T)))
proc reserveSlot[T](x: var seq[T]): ptr T =
if x.len >= x.cap: resize(x)
result = addr(x.data[x.len])
inc x.len
template add*[T](x: var seq[T]; y: T) =
reserveSlot(x)[] = y
proc shrink*[T](x: var seq[T]; newLen: int) =
assert newLen <= x.len
assert newLen >= 0
when not supportsCopyMem(T):
for i in countdown(x.len - 1, newLen - 1):
`=destroy`(x.data[i])
x.len = newLen
proc grow*[T](x: var seq[T]; newLen: int; value: T) =
if newLen <= x.len: return
assert newLen >= 0
if x.cap == 0: x.cap = newLen
else: x.cap = max(newLen, (x.cap * 3) shr 1)
x.data = cast[type(x.data)](realloc(x.data, x.cap * sizeof(T)))
for i in x.len..<newLen:
x.data[i] = value
x.len = newLen
template default[T](t: typedesc[T]): T =
var v: T
v
proc setLen*[T](x: var seq[T]; newLen: int) {.deprecated.} =
if newlen < x.len: shrink(x, newLen)
else: grow(x, newLen, default(T))
template `[]`*[T](x: seq[T]; i: Natural): T =
assert i < x.len
x.data[i]
template `[]=`*[T](x: seq[T]; i: Natural; y: T) =
assert i < x.len
x.data[i] = y
proc `@`*[T](elems: openArray[T]): seq[T] =
result.cap = elems.len
result.len = elems.len
result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
when supportsCopyMem(T):
copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T))
else:
for i in 0..<result.len:
result.data[i] = elems[i]
proc len*[T](x: seq[T]): int {.inline.} = x.len
proc `$`*[T](x: seq[T]): string =
result = "@["
var firstElement = true
for i in 0..<x.len:
let
value = x.data[i]
if firstElement:
firstElement = false
else:
result.add(", ")
when compiles(value.isNil):
# this branch should not be necessary
if value.isNil:
result.add "nil"
else:
result.addQuoted(value)
else:
result.addQuoted(value)
result.add("]")

111
lib/core/strs.nim Normal file
View file

@ -0,0 +1,111 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2017 Nim contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Default string implementation used by Nim's core.
import allocators
type
string {.core.} = object
len, cap: int
data: ptr UncheckedArray[char]
proc nimStringLiteral(x: cstring; len: int): string {.core.} =
string(len: len, cap: len, data: x)
template frees(s) = dealloc(s.data, s.cap + 1)
proc `=destroy`(s: var string) =
if s.data != nil:
frees(s)
s.data = nil
s.len = 0
s.cap = 0
proc `=sink`(a: var string, b: string) =
# we hope this is optimized away for not yet alive objects:
if a.data != nil and a.data != b.data:
frees(a)
a.len = b.len
a.cap = b.cap
a.data = b.data
proc `=`(a: var string; b: string) =
if a.data != nil and a.data != b.data:
frees(a)
a.data = nil
a.len = b.len
a.cap = b.cap
if b.data != nil:
a.data = cast[type(a.data)](alloc(a.cap + 1))
copyMem(a.data, b.data, a.cap+1)
proc resize(s: var string) =
let old = s.cap
if old == 0: s.cap = 8
else: s.cap = (s.cap * 3) shr 1
s.data = cast[type(s.data)](realloc(s.data, old + 1, s.cap + 1))
proc add*(s: var string; c: char) =
if s.len >= s.cap: resize(s)
s.data[s.len] = c
s.data[s.len+1] = '\0'
inc s.len
proc ensure(s: var string; newLen: int) =
let old = s.cap
if newLen >= old:
s.cap = max((old * 3) shr 1, newLen)
if s.cap > 0:
s.data = cast[type(s.data)](realloc(s.data, old + 1, s.cap + 1))
proc add*(s: var string; y: string) =
if y.len != 0:
let newLen = s.len + y.len
ensure(s, newLen)
copyMem(addr s.data[len], y.data, y.data.len + 1)
s.len = newLen
proc len*(s: string): int {.inline.} = s.len
proc newString*(len: int): string =
result.len = len
result.cap = len
if len > 0:
result.data = alloc0(len+1)
converter toCString(x: string): cstring {.core.} =
if x.len == 0: cstring"" else: cast[cstring](x.data)
proc newStringOfCap*(cap: int): string =
result.len = 0
result.cap = cap
if cap > 0:
result.data = alloc(cap+1)
proc `&`*(a, b: string): string =
let sum = a.len + b.len
result = newStringOfCap(sum)
result.len = sum
copyMem(addr result.data[0], a.data, a.len)
copyMem(addr result.data[a.len], b.data, b.len)
if sum > 0:
result.data[sum] = '\0'
proc concat(x: openArray[string]): string {.core.} =
## used be the code generator to optimize 'x & y & z ...'
var sum = 0
for i in 0 ..< x.len: inc(sum, x[i].len)
result = newStringOfCap(sum)
sum = 0
for i in 0 ..< x.len:
let L = x[i].len
copyMem(addr result.data[sum], x[i].data, L)
inc(sum, L)

19
lib/core/typelayouts.nim Normal file
View file

@ -0,0 +1,19 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2017 Nim contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
type
TypeLayout* = object
size*, alignment*: int
destructor*: proc (self: pointer; a: Allocator) {.nimcall.}
trace*: proc (self: pointer; a: Allocator) {.nimcall.}
when false:
construct*: proc (self: pointer; a: Allocator) {.nimcall.}
copy*, deepcopy*, sink*: proc (self, other: pointer; a: Allocator) {.nimcall.}
proc getTypeLayout(t: typedesc): ptr TypeLayout {.magic: "getTypeLayout".}

141
lib/js/asyncjs.nim Normal file
View file

@ -0,0 +1,141 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2017 Nim Authors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
## This module implements types and macros for writing asynchronous code
## for the JS backend. It provides tools for interaction with JavaScript async API-s
## and libraries, writing async procedures in Nim and converting callback-based code
## to promises.
##
## A Nim procedure is asynchronous when it includes the ``{.async.}`` pragma. It
## should always have a ``Future[T]`` return type or not have a return type at all.
## A ``Future[void]`` return type is assumed by default.
##
## This is roughly equivalent to the ``async`` keyword in JavaScript code.
##
## .. code-block:: nim
## proc loadGame(name: string): Future[Game] {.async.} =
## # code
##
## should be equivalent to
##
## .. code-block:: javascript
## async function loadGame(name) {
## // code
## }
##
## A call to an asynchronous procedure usually needs ``await`` to wait for
## the completion of the ``Future``.
##
## .. code-block:: nim
## var game = await loadGame(name)
##
## Often, you might work with callback-based API-s. You can wrap them with
## asynchronous procedures using promises and ``newPromise``:
##
## .. code-block:: nim
## proc loadGame(name: string): Future[Game] =
## var promise = newPromise() do (resolve: proc(response: Game)):
## cbBasedLoadGame(name) do (game: Game):
## resolve(game)
## return promise
##
## Forward definitions work properly, you just need to always add the ``{.async.}`` pragma:
##
## .. code-block:: nim
## proc loadGame(name: string): Future[Game] {.async.}
##
## JavaScript compatibility
## ~~~~~~~~~~~~~~~~~~~~~~~~~
##
## Nim currently generates `async/await` JavaScript code which is supported in modern
## EcmaScript and most modern versions of browsers, Node.js and Electron.
## If you need to use this module with older versions of JavaScript, you can
## use a tool that backports the resulting JavaScript code, as babel.
import jsffi
import macros
when not defined(js) and not defined(nimdoc) and not defined(nimsuggest):
{.fatal: "Module asyncjs is designed to be used with the JavaScript backend.".}
type
Future*[T] = ref object
future*: T
## Wraps the return type of an asynchronous procedure.
PromiseJs* {.importcpp: "Promise".} = ref object
## A JavaScript Promise
proc replaceReturn(node: var NimNode) =
var z = 0
for s in node:
var son = node[z]
if son.kind == nnkReturnStmt:
node[z] = nnkReturnStmt.newTree(nnkCall.newTree(ident("jsResolve"), son[0]))
elif son.kind == nnkAsgn and son[0].kind == nnkIdent and $son[0] == "result":
node[z] = nnkAsgn.newTree(son[0], nnkCall.newTree(ident("jsResolve"), son[1]))
else:
replaceReturn(son)
inc z
proc isFutureVoid(node: NimNode): bool =
result = node.kind == nnkBracketExpr and
node[0].kind == nnkIdent and $node[0] == "Future" and
node[1].kind == nnkIdent and $node[1] == "void"
proc generateJsasync(arg: NimNode): NimNode =
assert arg.kind == nnkProcDef
result = arg
var isVoid = false
var jsResolveNode = ident("jsResolve")
if arg.params[0].kind == nnkEmpty:
result.params[0] = nnkBracketExpr.newTree(ident("Future"), ident("void"))
isVoid = true
elif isFutureVoid(arg.params[0]):
isVoid = true
var code = result.body
replaceReturn(code)
result.body = nnkStmtList.newTree()
if len(code) > 0:
var awaitFunction = quote:
proc await[T](f: Future[T]): T {.importcpp: "(await #)".}
result.body.add(awaitFunction)
var resolve: NimNode
if isVoid:
resolve = quote:
var `jsResolveNode` {.importcpp: "undefined".}: Future[void]
else:
resolve = quote:
proc jsResolve[T](a: T): Future[T] {.importcpp: "#".}
result.body.add(resolve)
else:
result.body = newEmptyNode()
for child in code:
result.body.add(child)
if len(code) > 0 and isVoid:
var voidFix = quote:
return `jsResolveNode`
result.body.add(voidFix)
result.pragma = quote:
{.codegenDecl: "async function $2($3)".}
macro async*(arg: untyped): untyped =
## Macro which converts normal procedures into
## javascript-compatible async procedures
generateJsasync(arg)
proc newPromise*[T](handler: proc(resolve: proc(response: T))): Future[T] {.importcpp: "(new Promise(#))".}
## A helper for wrapping callback-based functions
## into promises and async procedures

View file

@ -134,9 +134,9 @@ type
# https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement # https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
HtmlElement* = ref object of Element HtmlElement* = ref object of Element
contentEditable*: string contentEditable*: cstring
isContentEditable*: bool isContentEditable*: bool
dir*: string dir*: cstring
offsetHeight*: int offsetHeight*: int
offsetWidth*: int offsetWidth*: int
offsetLeft*: int offsetLeft*: int
@ -405,7 +405,7 @@ type
# EventTarget "methods" # EventTarget "methods"
proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false) proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), options: AddEventListenerOptions) proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), options: AddEventListenerOptions)
proc removeEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
# Window "methods" # Window "methods"
proc alert*(w: Window, msg: cstring) proc alert*(w: Window, msg: cstring)
@ -507,6 +507,7 @@ proc replace*(loc: Location, s: cstring)
proc back*(h: History) proc back*(h: History)
proc forward*(h: History) proc forward*(h: History)
proc go*(h: History, pagesToJump: int) proc go*(h: History, pagesToJump: int)
proc pushState*[T](h: History, stateObject: T, title, url: cstring)
# Navigator "methods" # Navigator "methods"
proc javaEnabled*(h: Navigator): bool proc javaEnabled*(h: Navigator): bool

View file

@ -70,7 +70,7 @@ __clang__
#if defined(_MSC_VER) #if defined(_MSC_VER)
# pragma warning(disable: 4005 4100 4101 4189 4191 4200 4244 4293 4296 4309) # pragma warning(disable: 4005 4100 4101 4189 4191 4200 4244 4293 4296 4309)
# pragma warning(disable: 4310 4365 4456 4477 4514 4574 4611 4668 4702 4706) # pragma warning(disable: 4310 4365 4456 4477 4514 4574 4611 4668 4702 4706)
# pragma warning(disable: 4710 4711 4774 4800 4820 4996 4090 4297) # pragma warning(disable: 4710 4711 4774 4800 4809 4820 4996 4090 4297)
#endif #endif
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
@ -159,6 +159,7 @@ __clang__
/* ------------------------------------------------------------------- */ /* ------------------------------------------------------------------- */
#if defined(WIN32) || defined(_WIN32) /* only Windows has this mess... */ #if defined(WIN32) || defined(_WIN32) /* only Windows has this mess... */
# define N_LIB_PRIVATE
# define N_CDECL(rettype, name) rettype __cdecl name # define N_CDECL(rettype, name) rettype __cdecl name
# define N_STDCALL(rettype, name) rettype __stdcall name # define N_STDCALL(rettype, name) rettype __stdcall name
# define N_SYSCALL(rettype, name) rettype __syscall name # define N_SYSCALL(rettype, name) rettype __syscall name
@ -178,6 +179,7 @@ __clang__
# endif # endif
# define N_LIB_IMPORT extern __declspec(dllimport) # define N_LIB_IMPORT extern __declspec(dllimport)
#else #else
# define N_LIB_PRIVATE __attribute__((visibility("hidden")))
# if defined(__GNUC__) # if defined(__GNUC__)
# define N_CDECL(rettype, name) rettype name # define N_CDECL(rettype, name) rettype name
# define N_STDCALL(rettype, name) rettype name # define N_STDCALL(rettype, name) rettype name
@ -398,11 +400,11 @@ typedef struct TStringDesc* string;
// NAN definition copied from math.h included in the Windows SDK version 10.0.14393.0 // NAN definition copied from math.h included in the Windows SDK version 10.0.14393.0
#ifndef NAN #ifndef NAN
#ifndef _HUGE_ENUF # ifndef _HUGE_ENUF
#define _HUGE_ENUF 1e+300 // _HUGE_ENUF*_HUGE_ENUF must overflow # define _HUGE_ENUF 1e+300 // _HUGE_ENUF*_HUGE_ENUF must overflow
#endif # endif
#define NAN_INFINITY ((float)(_HUGE_ENUF * _HUGE_ENUF)) # define NAN_INFINITY ((float)(_HUGE_ENUF * _HUGE_ENUF))
#define NAN ((float)(NAN_INFINITY * 0.0F)) # define NAN ((float)(NAN_INFINITY * 0.0F))
#endif #endif
#ifndef INF #ifndef INF
@ -480,7 +482,6 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); }
On disagreement, your C compiler will say something like: On disagreement, your C compiler will say something like:
"error: 'Nim_and_C_compiler_disagree_on_target_architecture' declared as an array with a negative size" */ "error: 'Nim_and_C_compiler_disagree_on_target_architecture' declared as an array with a negative size" */
typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1]; typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];
#endif
#ifdef __cplusplus #ifdef __cplusplus
# define NIM_EXTERNC extern "C" # define NIM_EXTERNC extern "C"
@ -507,3 +508,5 @@ extern Libc::Env *genodeEnv;
/* Compile with -d:checkAbi and a sufficiently C11:ish compiler to enable */ /* Compile with -d:checkAbi and a sufficiently C11:ish compiler to enable */
#define NIM_CHECK_SIZE(typ, sz) \ #define NIM_CHECK_SIZE(typ, sz) \
_Static_assert(sizeof(typ) == sz, "Nim & C disagree on type size") _Static_assert(sizeof(typ) == sz, "Nim & C disagree on type size")
#endif /* NIMBASE_H */

View file

@ -609,11 +609,12 @@ proc clock_nanosleep*(a1: ClockId, a2: cint, a3: var Timespec,
proc clock_settime*(a1: ClockId, a2: var Timespec): cint {. proc clock_settime*(a1: ClockId, a2: var Timespec): cint {.
importc, header: "<time.h>".} importc, header: "<time.h>".}
proc `==`*(a, b: Time): bool {.borrow.}
proc `-`*(a, b: Time): Time {.borrow.}
proc ctime*(a1: var Time): cstring {.importc, header: "<time.h>".} proc ctime*(a1: var Time): cstring {.importc, header: "<time.h>".}
proc ctime_r*(a1: var Time, a2: cstring): cstring {.importc, header: "<time.h>".} proc ctime_r*(a1: var Time, a2: cstring): cstring {.importc, header: "<time.h>".}
proc difftime*(a1, a2: Time): cdouble {.importc, header: "<time.h>".} proc difftime*(a1, a2: Time): cdouble {.importc, header: "<time.h>".}
proc getdate*(a1: cstring): ptr Tm {.importc, header: "<time.h>".} proc getdate*(a1: cstring): ptr Tm {.importc, header: "<time.h>".}
proc gmtime*(a1: var Time): ptr Tm {.importc, header: "<time.h>".} proc gmtime*(a1: var Time): ptr Tm {.importc, header: "<time.h>".}
proc gmtime_r*(a1: var Time, a2: var Tm): ptr Tm {.importc, header: "<time.h>".} proc gmtime_r*(a1: var Time, a2: var Tm): ptr Tm {.importc, header: "<time.h>".}
proc localtime*(a1: var Time): ptr Tm {.importc, header: "<time.h>".} proc localtime*(a1: var Time): ptr Tm {.importc, header: "<time.h>".}

View file

@ -12,8 +12,6 @@
# To be included from posix.nim! # To be included from posix.nim!
from times import Time
const const
hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays
hasAioH = defined(linux) hasAioH = defined(linux)
@ -40,13 +38,15 @@ type
const SIG_HOLD* = cast[SigHandler](2) const SIG_HOLD* = cast[SigHandler](2)
type type
Time* {.importc: "time_t", header: "<time.h>".} = distinct clong
Timespec* {.importc: "struct timespec", Timespec* {.importc: "struct timespec",
header: "<time.h>", final, pure.} = object ## struct timespec header: "<time.h>", final, pure.} = object ## struct timespec
tv_sec*: Time ## Seconds. tv_sec*: Time ## Seconds.
tv_nsec*: clong ## Nanoseconds. tv_nsec*: clong ## Nanoseconds.
Dirent* {.importc: "struct dirent", Dirent* {.importc: "struct dirent",
header: "<dirent.h>", final, pure.} = object ## dirent_t struct header: "<dirent.h>", final, pure.} = object ## dirent_t struct
d_ino*: Ino d_ino*: Ino
d_off*: Off d_off*: Off
d_reclen*: cushort d_reclen*: cushort

View file

@ -9,8 +9,6 @@
{.deadCodeElim:on.} {.deadCodeElim:on.}
from times import Time
const const
hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays
hasAioH = defined(linux) hasAioH = defined(linux)
@ -36,6 +34,8 @@ type
{.deprecated: [TSocketHandle: SocketHandle].} {.deprecated: [TSocketHandle: SocketHandle].}
type type
Time* {.importc: "time_t", header: "<time.h>".} = distinct clong
Timespec* {.importc: "struct timespec", Timespec* {.importc: "struct timespec",
header: "<time.h>", final, pure.} = object ## struct timespec header: "<time.h>", final, pure.} = object ## struct timespec
tv_sec*: Time ## Seconds. tv_sec*: Time ## Seconds.
@ -209,24 +209,24 @@ type
st_gid*: Gid ## Group ID of file. st_gid*: Gid ## Group ID of file.
st_rdev*: Dev ## Device ID (if file is character or block special). st_rdev*: Dev ## Device ID (if file is character or block special).
st_size*: Off ## For regular files, the file size in bytes. st_size*: Off ## For regular files, the file size in bytes.
## For symbolic links, the length in bytes of the ## For symbolic links, the length in bytes of the
## pathname contained in the symbolic link. ## pathname contained in the symbolic link.
## For a shared memory object, the length in bytes. ## For a shared memory object, the length in bytes.
## For a typed memory object, the length in bytes. ## For a typed memory object, the length in bytes.
## For other file types, the use of this field is ## For other file types, the use of this field is
## unspecified. ## unspecified.
when defined(macosx) or defined(android): when defined(macosx) or defined(android):
st_atime*: Time ## Time of last access. st_atime*: Time ## Time of last access.
st_mtime*: Time ## Time of last data modification. st_mtime*: Time ## Time of last data modification.
st_ctime*: Time ## Time of last status change. st_ctime*: Time ## Time of last status change.
else: else:
st_atim*: Timespec ## Time of last access. st_atim*: Timespec ## Time of last access.
st_mtim*: Timespec ## Time of last data modification. st_mtim*: Timespec ## Time of last data modification.
st_ctim*: Timespec ## Time of last status change. st_ctim*: Timespec ## Time of last status change.
st_blksize*: Blksize ## A file system-specific preferred I/O block size st_blksize*: Blksize ## A file system-specific preferred I/O block size
## for this object. In some file system types, this ## for this object. In some file system types, this
## may vary from file to file. ## may vary from file to file.
st_blocks*: Blkcnt ## Number of blocks allocated for this object. st_blocks*: Blkcnt ## Number of blocks allocated for this object.
Statvfs* {.importc: "struct statvfs", header: "<sys/statvfs.h>", Statvfs* {.importc: "struct statvfs", header: "<sys/statvfs.h>",

View file

@ -168,18 +168,20 @@ type
timers*: HeapQueue[tuple[finishAt: float, fut: Future[void]]] timers*: HeapQueue[tuple[finishAt: float, fut: Future[void]]]
callbacks*: Deque[proc ()] callbacks*: Deque[proc ()]
proc processTimers(p: PDispatcherBase) {.inline.} = proc processTimers(p: PDispatcherBase; didSomeWork: var bool) {.inline.} =
#Process just part if timers at a step #Process just part if timers at a step
var count = p.timers.len var count = p.timers.len
let t = epochTime() let t = epochTime()
while count > 0 and t >= p.timers[0].finishAt: while count > 0 and t >= p.timers[0].finishAt:
p.timers.pop().fut.complete() p.timers.pop().fut.complete()
dec count dec count
didSomeWork = true
proc processPendingCallbacks(p: PDispatcherBase) = proc processPendingCallbacks(p: PDispatcherBase; didSomeWork: var bool) =
while p.callbacks.len > 0: while p.callbacks.len > 0:
var cb = p.callbacks.popFirst() var cb = p.callbacks.popFirst()
cb() cb()
didSomeWork = true
proc adjustedTimeout(p: PDispatcherBase, timeout: int): int {.inline.} = proc adjustedTimeout(p: PDispatcherBase, timeout: int): int {.inline.} =
# If dispatcher has active timers this proc returns the timeout # If dispatcher has active timers this proc returns the timeout
@ -298,14 +300,13 @@ when defined(windows) or defined(nimdoc):
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
p.handles.len != 0 or p.timers.len != 0 or p.callbacks.len != 0 p.handles.len != 0 or p.timers.len != 0 or p.callbacks.len != 0
proc poll*(timeout = 500) = proc runOnce(timeout = 500): bool =
## Waits for completion events and processes them. Raises ``ValueError``
## if there are no pending operations.
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
if p.handles.len == 0 and p.timers.len == 0 and p.callbacks.len == 0: if p.handles.len == 0 and p.timers.len == 0 and p.callbacks.len == 0:
raise newException(ValueError, raise newException(ValueError,
"No handles or timers registered in dispatcher.") "No handles or timers registered in dispatcher.")
result = false
if p.handles.len != 0: if p.handles.len != 0:
let at = p.adjustedTimeout(timeout) let at = p.adjustedTimeout(timeout)
var llTimeout = var llTimeout =
@ -318,6 +319,7 @@ when defined(windows) or defined(nimdoc):
let res = getQueuedCompletionStatus(p.ioPort, let res = getQueuedCompletionStatus(p.ioPort,
addr lpNumberOfBytesTransferred, addr lpCompletionKey, addr lpNumberOfBytesTransferred, addr lpCompletionKey,
cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool
result = true
# http://stackoverflow.com/a/12277264/492186 # http://stackoverflow.com/a/12277264/492186
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
@ -347,13 +349,14 @@ when defined(windows) or defined(nimdoc):
else: else:
if errCode.int32 == WAIT_TIMEOUT: if errCode.int32 == WAIT_TIMEOUT:
# Timed out # Timed out
discard result = false
else: raiseOSError(errCode) else: raiseOSError(errCode)
# Timer processing. # Timer processing.
processTimers(p) processTimers(p, result)
# Callback queue processing # Callback queue processing
processPendingCallbacks(p) processPendingCallbacks(p, result)
var acceptEx: WSAPROC_ACCEPTEX var acceptEx: WSAPROC_ACCEPTEX
var connectEx: WSAPROC_CONNECTEX var connectEx: WSAPROC_CONNECTEX
@ -1229,7 +1232,7 @@ else:
# descriptor was unregistered in callback via `unregister()`. # descriptor was unregistered in callback via `unregister()`.
discard discard
proc poll*(timeout = 500) = proc runOnce(timeout = 500): bool =
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
when ioselSupportedPlatform: when ioselSupportedPlatform:
let customSet = {Event.Timer, Event.Signal, Event.Process, let customSet = {Event.Timer, Event.Signal, Event.Process,
@ -1239,6 +1242,7 @@ else:
raise newException(ValueError, raise newException(ValueError,
"No handles or timers registered in dispatcher.") "No handles or timers registered in dispatcher.")
result = false
if not p.selector.isEmpty(): if not p.selector.isEmpty():
var keys: array[64, ReadyKey] var keys: array[64, ReadyKey]
var count = p.selector.selectInto(p.adjustedTimeout(timeout), keys) var count = p.selector.selectInto(p.adjustedTimeout(timeout), keys)
@ -1251,20 +1255,24 @@ else:
if Event.Read in events or events == {Event.Error}: if Event.Read in events or events == {Event.Error}:
processBasicCallbacks(fd, readList) processBasicCallbacks(fd, readList)
result = true
if Event.Write in events or events == {Event.Error}: if Event.Write in events or events == {Event.Error}:
processBasicCallbacks(fd, writeList) processBasicCallbacks(fd, writeList)
result = true
if Event.User in events or events == {Event.Error}: if Event.User in events:
processBasicCallbacks(fd, readList) processBasicCallbacks(fd, readList)
custom = true custom = true
if rLength == 0: if rLength == 0:
p.selector.unregister(fd) p.selector.unregister(fd)
result = true
when ioselSupportedPlatform: when ioselSupportedPlatform:
if (customSet * events) != {}: if (customSet * events) != {}:
custom = true custom = true
processCustomCallbacks(fd) processCustomCallbacks(fd)
result = true
# because state `data` can be modified in callback we need to update # because state `data` can be modified in callback we need to update
# descriptor events with currently registered callbacks. # descriptor events with currently registered callbacks.
@ -1276,9 +1284,9 @@ else:
p.selector.updateHandle(SocketHandle(fd), newEvents) p.selector.updateHandle(SocketHandle(fd), newEvents)
# Timer processing. # Timer processing.
processTimers(p) processTimers(p, result)
# Callback queue processing # Callback queue processing
processPendingCallbacks(p) processPendingCallbacks(p, result)
proc recv*(socket: AsyncFD, size: int, proc recv*(socket: AsyncFD, size: int,
flags = {SocketFlag.SafeDisconn}): Future[string] = flags = {SocketFlag.SafeDisconn}): Future[string] =
@ -1501,6 +1509,19 @@ else:
data.readList.add(cb) data.readList.add(cb)
p.selector.registerEvent(SelectEvent(ev), data) p.selector.registerEvent(SelectEvent(ev), data)
proc drain*(timeout = 500) =
## Waits for completion events and processes them. Raises ``ValueError``
## if there are no pending operations. In contrast to ``poll`` this
## processes as many events as are available.
if runOnce(timeout):
while hasPendingOperations() and runOnce(0): discard
proc poll*(timeout = 500) =
## Waits for completion events and processes them. Raises ``ValueError``
## if there are no pending operations. This runs the underlying OS
## `epoll`:idx: or `kqueue`:idx: primitive only once.
discard runOnce(timeout)
# Common procedures between current and upcoming asyncdispatch # Common procedures between current and upcoming asyncdispatch
include includes.asynccommon include includes.asynccommon

View file

@ -1,4 +1,4 @@
import os, tables, strutils, times, heapqueue, options, deques import os, tables, strutils, times, heapqueue, options, deques, cstrutils
# TODO: This shouldn't need to be included, but should ideally be exported. # TODO: This shouldn't need to be included, but should ideally be exported.
type type
@ -217,17 +217,78 @@ proc `callback=`*[T](future: Future[T],
## If future has already completed then ``cb`` will be called immediately. ## If future has already completed then ``cb`` will be called immediately.
future.callback = proc () = cb(future) future.callback = proc () = cb(future)
proc injectStacktrace[T](future: Future[T]) = proc getHint(entry: StackTraceEntry): string =
# TODO: Come up with something better. ## We try to provide some hints about stack trace entries that the user
when not defined(release): ## may not be familiar with, in particular calls inside the stdlib.
var msg = "" result = ""
msg.add("\n " & future.fromProc & "'s lead up to read of failed Future:") if entry.procname == "processPendingCallbacks":
if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0:
return "Executes pending callbacks"
elif entry.procname == "poll":
if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0:
return "Processes asynchronous completion events"
if not future.errorStackTrace.isNil and future.errorStackTrace != "": if entry.procname.endsWith("_continue"):
msg.add("\n" & indent(future.errorStackTrace.strip(), 4)) if cmpIgnoreStyle(entry.filename, "asyncmacro.nim") == 0:
else: return "Resumes an async procedure"
msg.add("\n Empty or nil stack trace.")
future.error.msg.add(msg) proc `$`*(entries: seq[StackTraceEntry]): string =
result = ""
# Find longest filename & line number combo for alignment purposes.
var longestLeft = 0
for entry in entries:
if entry.procName.isNil: continue
let left = $entry.filename & $entry.line
if left.len > longestLeft:
longestLeft = left.len
var indent = 2
# Format the entries.
for entry in entries:
if entry.procName.isNil:
if entry.line == -10:
result.add(spaces(indent) & "#[\n")
indent.inc(2)
else:
indent.dec(2)
result.add(spaces(indent)& "]#\n")
continue
let left = "$#($#)" % [$entry.filename, $entry.line]
result.add((spaces(indent) & "$#$# $#\n") % [
left,
spaces(longestLeft - left.len + 2),
$entry.procName
])
let hint = getHint(entry)
if hint.len > 0:
result.add(spaces(indent+2) & "## " & hint & "\n")
proc injectStacktrace[T](future: Future[T]) =
when not defined(release):
const header = "\nAsync traceback:\n"
var exceptionMsg = future.error.msg
if header in exceptionMsg:
# This is messy: extract the original exception message from the msg
# containing the async traceback.
let start = exceptionMsg.find(header)
exceptionMsg = exceptionMsg[0..<start]
var newMsg = exceptionMsg & header
let entries = getStackTraceEntries(future.error)
newMsg.add($entries)
newMsg.add("Exception message: " & exceptionMsg & "\n")
newMsg.add("Exception type:")
# # For debugging purposes
# for entry in getStackTraceEntries(future.error):
# newMsg.add "\n" & $entry
future.error.msg = newMsg
proc read*[T](future: Future[T] | FutureVar[T]): T = proc read*[T](future: Future[T] | FutureVar[T]): T =
## Retrieves the value of ``future``. Future must be finished otherwise ## Retrieves the value of ``future``. Future must be finished otherwise
@ -263,12 +324,12 @@ proc mget*[T](future: FutureVar[T]): var T =
## Future has not been finished. ## Future has not been finished.
result = Future[T](future).value result = Future[T](future).value
proc finished*[T](future: Future[T] | FutureVar[T]): bool = proc finished*(future: FutureBase | FutureVar): bool =
## Determines whether ``future`` has completed. ## Determines whether ``future`` has completed.
## ##
## ``True`` may indicate an error or a value. Use ``failed`` to distinguish. ## ``True`` may indicate an error or a value. Use ``failed`` to distinguish.
when future is FutureVar[T]: when future is FutureVar:
result = (Future[T](future)).finished result = (FutureBase(future)).finished
else: else:
result = future.finished result = future.finished

View file

@ -25,22 +25,28 @@ proc skipStmtList(node: NimNode): NimNode {.compileTime.} =
result = node[0] result = node[0]
template createCb(retFutureSym, iteratorNameSym, template createCb(retFutureSym, iteratorNameSym,
name, futureVarCompletions: untyped) = strName, identName, futureVarCompletions: untyped) =
var nameIterVar = iteratorNameSym var nameIterVar = iteratorNameSym
#{.push stackTrace: off.} #{.push stackTrace: off.}
proc cb0 {.closure.} = proc identName {.closure.} =
try: try:
if not nameIterVar.finished: if not nameIterVar.finished:
var next = nameIterVar() var next = nameIterVar()
# Continue while the yielded future is already finished.
while (not next.isNil) and next.finished:
next = nameIterVar()
if nameIterVar.finished:
break
if next == nil: if next == nil:
if not retFutureSym.finished: if not retFutureSym.finished:
let msg = "Async procedure ($1) yielded `nil`, are you await'ing a " & let msg = "Async procedure ($1) yielded `nil`, are you await'ing a " &
"`nil` Future?" "`nil` Future?"
raise newException(AssertionError, msg % name) raise newException(AssertionError, msg % strName)
else: else:
{.gcsafe.}: {.gcsafe.}:
{.push hint[ConvFromXtoItselfNotNeeded]: off.} {.push hint[ConvFromXtoItselfNotNeeded]: off.}
next.callback = (proc() {.closure, gcsafe.})(cb0) next.callback = (proc() {.closure, gcsafe.})(identName)
{.pop.} {.pop.}
except: except:
futureVarCompletions futureVarCompletions
@ -52,7 +58,7 @@ template createCb(retFutureSym, iteratorNameSym,
else: else:
retFutureSym.fail(getCurrentException()) retFutureSym.fail(getCurrentException())
cb0() identName()
#{.pop.} #{.pop.}
proc generateExceptionCheck(futSym, proc generateExceptionCheck(futSym,
tryStmt, rootReceiver, fromNode: NimNode): NimNode {.compileTime.} = tryStmt, rootReceiver, fromNode: NimNode): NimNode {.compileTime.} =
@ -389,9 +395,12 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
outerProcBody.add(closureIterator) outerProcBody.add(closureIterator)
# -> createCb(retFuture) # -> createCb(retFuture)
#var cbName = newIdentNode("cb") # NOTE: The "_continue" suffix is checked for in asyncfutures.nim to produce
# friendlier stack traces:
var cbName = genSym(nskProc, prcName & "_continue")
var procCb = getAst createCb(retFutureSym, iteratorNameSym, var procCb = getAst createCb(retFutureSym, iteratorNameSym,
newStrLitNode(prcName), newStrLitNode(prcName),
cbName,
createFutureVarCompletions(futureVarIdents, nil)) createFutureVarCompletions(futureVarIdents, nil))
outerProcBody.add procCb outerProcBody.add procCb

View file

@ -286,6 +286,7 @@ template readInto(buf: pointer, size: int, socket: AsyncSocket,
flags: set[SocketFlag]): int = flags: set[SocketFlag]): int =
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``. Note that ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``. Note that
## this is a template and not a proc. ## this is a template and not a proc.
assert(not socket.closed, "Cannot `recv` on a closed socket")
var res = 0 var res = 0
if socket.isSsl: if socket.isSsl:
when defineSsl: when defineSsl:
@ -412,6 +413,7 @@ proc send*(socket: AsyncSocket, buf: pointer, size: int,
## Sends ``size`` bytes from ``buf`` to ``socket``. The returned future will complete once all ## Sends ``size`` bytes from ``buf`` to ``socket``. The returned future will complete once all
## data has been sent. ## data has been sent.
assert socket != nil assert socket != nil
assert(not socket.closed, "Cannot `send` on a closed socket")
if socket.isSsl: if socket.isSsl:
when defineSsl: when defineSsl:
sslLoop(socket, flags, sslLoop(socket, flags,

View file

@ -141,8 +141,8 @@ proc excl*[T](c: var CritBitTree[T], key: string) =
proc missingOrExcl*[T](c: var CritBitTree[T], key: string): bool = proc missingOrExcl*[T](c: var CritBitTree[T], key: string): bool =
## Returns true iff `c` does not contain the given `key`. If the key ## Returns true iff `c` does not contain the given `key`. If the key
## does exist, c.excl(key) is performed. ## does exist, c.excl(key) is performed.
let oldCount = c.count let oldCount = c.count
var n = exclImpl(c, key) var n = exclImpl(c, key)
result = c.count == oldCount result = c.count == oldCount
@ -326,7 +326,7 @@ proc `$`*[T](c: CritBitTree[T]): string =
result.add($key) result.add($key)
when T isnot void: when T isnot void:
result.add(": ") result.add(": ")
result.add($val) result.addQuoted(val)
result.add("}") result.add("}")
when isMainModule: when isMainModule:

View file

@ -185,7 +185,7 @@ proc `$`*[T](deq: Deque[T]): string =
result = "[" result = "["
for x in deq: for x in deq:
if result.len > 1: result.add(", ") if result.len > 1: result.add(", ")
result.add($x) result.addQuoted(x)
result.add("]") result.add("]")
when isMainModule: when isMainModule:

View file

@ -135,7 +135,7 @@ proc `$`*[T](L: SomeLinkedCollection[T]): string =
result = "[" result = "["
for x in nodes(L): for x in nodes(L):
if result.len > 1: result.add(", ") if result.len > 1: result.add(", ")
result.add($x.value) result.addQuoted(x.value)
result.add("]") result.add("]")
proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] = proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] =

View file

@ -406,7 +406,7 @@ template dollarImpl() {.dirty.} =
result = "{" result = "{"
for key in items(s): for key in items(s):
if result.len > 1: result.add(", ") if result.len > 1: result.add(", ")
result.add($key) result.addQuoted(key)
result.add("}") result.add("}")
proc `$`*[A](s: HashSet[A]): string = proc `$`*[A](s: HashSet[A]): string =

View file

@ -73,10 +73,10 @@ proc add*[A](x: var SharedList[A]; y: A) =
node.d[node.dataLen] = y node.d[node.dataLen] = y
inc(node.dataLen) inc(node.dataLen)
proc initSharedList*[A](): SharedList[A] = proc init*[A](t: var SharedList[A]) =
initLock result.lock initLock t.lock
result.head = nil t.head = nil
result.tail = nil t.tail = nil
proc clear*[A](t: var SharedList[A]) = proc clear*[A](t: var SharedList[A]) =
withLock(t): withLock(t):
@ -92,4 +92,11 @@ proc deinitSharedList*[A](t: var SharedList[A]) =
clear(t) clear(t)
deinitLock t.lock deinitLock t.lock
proc initSharedList*[A](): SharedList[A] {.deprecated.} =
## Deprecated. Use `init` instead.
## This is not posix compliant, may introduce undefined behavior.
initLock result.lock
result.head = nil
result.tail = nil
{.pop.} {.pop.}

View file

@ -183,6 +183,7 @@ proc `[]=`*[A, B](t: var SharedTable[A, B], key: A, val: B) =
proc add*[A, B](t: var SharedTable[A, B], key: A, val: B) = proc add*[A, B](t: var SharedTable[A, B], key: A, val: B) =
## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
## This can introduce duplicate keys into the table!
withLock t: withLock t:
addImpl(enlarge) addImpl(enlarge)
@ -191,19 +192,29 @@ proc del*[A, B](t: var SharedTable[A, B], key: A) =
withLock t: withLock t:
delImpl() delImpl()
proc initSharedTable*[A, B](initialSize=64): SharedTable[A, B] = proc init*[A, B](t: var SharedTable[A, B], initialSize=64) =
## creates a new hash table that is empty. ## creates a new hash table that is empty.
## ##
## `initialSize` needs to be a power of two. If you need to accept runtime ## `initialSize` needs to be a power of two. If you need to accept runtime
## values for this you could use the ``nextPowerOfTwo`` proc from the ## values for this you could use the ``nextPowerOfTwo`` proc from the
## `math <math.html>`_ module or the ``rightSize`` proc from this module. ## `math <math.html>`_ module or the ``rightSize`` proc from this module.
assert isPowerOfTwo(initialSize) assert isPowerOfTwo(initialSize)
result.counter = 0 t.counter = 0
result.dataLen = initialSize t.dataLen = initialSize
result.data = cast[KeyValuePairSeq[A, B]](allocShared0( t.data = cast[KeyValuePairSeq[A, B]](allocShared0(
sizeof(KeyValuePair[A, B]) * initialSize)) sizeof(KeyValuePair[A, B]) * initialSize))
initLock result.lock initLock t.lock
proc deinitSharedTable*[A, B](t: var SharedTable[A, B]) = proc deinitSharedTable*[A, B](t: var SharedTable[A, B]) =
deallocShared(t.data) deallocShared(t.data)
deinitLock t.lock deinitLock t.lock
proc initSharedTable*[A, B](initialSize=64): SharedTable[A, B] {.deprecated.} =
## Deprecated. Use `init` instead.
## This is not posix compliant, may introduce undefined behavior.
assert isPowerOfTwo(initialSize)
result.counter = 0
result.dataLen = initialSize
result.data = cast[KeyValuePairSeq[A, B]](allocShared0(
sizeof(KeyValuePair[A, B]) * initialSize))
initLock result.lock

View file

@ -308,6 +308,7 @@ proc `[]=`*[A, B](t: var Table[A, B], key: A, val: B) =
proc add*[A, B](t: var Table[A, B], key: A, val: B) = proc add*[A, B](t: var Table[A, B], key: A, val: B) =
## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
## This can introduce duplicate keys into the table!
addImpl(enlarge) addImpl(enlarge)
proc len*[A, B](t: TableRef[A, B]): int = proc len*[A, B](t: TableRef[A, B]): int =
@ -337,9 +338,9 @@ template dollarImpl(): untyped {.dirty.} =
result = "{" result = "{"
for key, val in pairs(t): for key, val in pairs(t):
if result.len > 1: result.add(", ") if result.len > 1: result.add(", ")
result.add($key) result.addQuoted(key)
result.add(": ") result.add(": ")
result.add($val) result.addQuoted(val)
result.add("}") result.add("}")
proc `$`*[A, B](t: Table[A, B]): string = proc `$`*[A, B](t: Table[A, B]): string =
@ -430,6 +431,7 @@ proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) =
proc add*[A, B](t: TableRef[A, B], key: A, val: B) = proc add*[A, B](t: TableRef[A, B], key: A, val: B) =
## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
## This can introduce duplicate keys into the table!
t[].add(key, val) t[].add(key, val)
proc del*[A, B](t: TableRef[A, B], key: A) = proc del*[A, B](t: TableRef[A, B], key: A) =
@ -604,6 +606,7 @@ proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
proc add*[A, B](t: var OrderedTable[A, B], key: A, val: B) = proc add*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
## This can introduce duplicate keys into the table!
addImpl(enlarge) addImpl(enlarge)
proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B = proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B =
@ -770,6 +773,7 @@ proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: B) = proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.
## This can introduce duplicate keys into the table!
t[].add(key, val) t[].add(key, val)
proc newOrderedTable*[A, B](initialSize=64): OrderedTableRef[A, B] = proc newOrderedTable*[A, B](initialSize=64): OrderedTableRef[A, B] =
@ -962,9 +966,10 @@ proc initCountTable*[A](initialSize=64): CountTable[A] =
newSeq(result.data, initialSize) newSeq(result.data, initialSize)
proc toCountTable*[A](keys: openArray[A]): CountTable[A] = proc toCountTable*[A](keys: openArray[A]): CountTable[A] =
## creates a new count table with every key in `keys` having a count of 1. ## creates a new count table with every key in `keys` having a count
## of how many times it occurs in `keys`.
result = initCountTable[A](rightSize(keys.len)) result = initCountTable[A](rightSize(keys.len))
for key in items(keys): result[key] = 1 for key in items(keys): result.inc key
proc `$`*[A](t: CountTable[A]): string = proc `$`*[A](t: CountTable[A]): string =
## The `$` operator for count tables. ## The `$` operator for count tables.
@ -989,9 +994,10 @@ proc inc*[A](t: var CountTable[A], key: A, val = 1) =
proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] = proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] =
## returns the (key,val)-pair with the smallest `val`. Efficiency: O(n) ## returns the (key,val)-pair with the smallest `val`. Efficiency: O(n)
assert t.len > 0 assert t.len > 0
var minIdx = 0 var minIdx = -1
for h in 1..high(t.data): for h in 0..high(t.data):
if t.data[h].val > 0 and t.data[minIdx].val > t.data[h].val: minIdx = h if t.data[h].val > 0 and (minIdx == -1 or t.data[minIdx].val > t.data[h].val):
minIdx = h
result.key = t.data[minIdx].key result.key = t.data[minIdx].key
result.val = t.data[minIdx].val result.val = t.data[minIdx].val
@ -1325,3 +1331,7 @@ when isMainModule:
assert((a == b) == true) assert((a == b) == true)
assert((b == a) == true) assert((b == a) == true)
block: # CountTable.smallest
var t = initCountTable[int]()
for v in items([0, 0, 5, 5, 5]): t.inc(v)
doAssert t.smallest == (0, 2)

View file

@ -51,7 +51,7 @@ proc setCookie*(key, value: string, domain = "", path = "",
if secure: result.add("; Secure") if secure: result.add("; Secure")
if httpOnly: result.add("; HttpOnly") if httpOnly: result.add("; HttpOnly")
proc setCookie*(key, value: string, expires: TimeInfo, proc setCookie*(key, value: string, expires: DateTime,
domain = "", path = "", noName = false, domain = "", path = "", noName = false,
secure = false, httpOnly = false): string = secure = false, httpOnly = false): string =
## Creates a command in the format of ## Creates a command in the format of
@ -63,9 +63,9 @@ proc setCookie*(key, value: string, expires: TimeInfo,
noname, secure, httpOnly) noname, secure, httpOnly)
when isMainModule: when isMainModule:
var tim = Time(int(getTime()) + 76 * (60 * 60 * 24)) var tim = fromUnix(getTime().toUnix + 76 * (60 * 60 * 24))
let cookie = setCookie("test", "value", tim.getGMTime()) let cookie = setCookie("test", "value", tim.utc)
when not defined(testing): when not defined(testing):
echo cookie echo cookie
let start = "Set-Cookie: test=value; Expires=" let start = "Set-Cookie: test=value; Expires="

79
lib/pure/cstrutils.nim Normal file
View file

@ -0,0 +1,79 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2017 Nim contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module supports helper routines for working with ``cstring``
## without having to convert ``cstring`` to ``string`` in order to
## save allocations.
include "system/inclrtl"
proc toLowerAscii(c: char): char {.inline.} =
if c in {'A'..'Z'}:
result = chr(ord(c) + (ord('a') - ord('A')))
else:
result = c
proc startsWith*(s, prefix: cstring): bool {.noSideEffect,
rtl, extern: "csuStartsWith".} =
## Returns true iff ``s`` starts with ``prefix``.
##
## If ``prefix == ""`` true is returned.
var i = 0
while true:
if prefix[i] == '\0': return true
if s[i] != prefix[i]: return false
inc(i)
proc endsWith*(s, suffix: cstring): bool {.noSideEffect,
rtl, extern: "csuEndsWith".} =
## Returns true iff ``s`` ends with ``suffix``.
##
## If ``suffix == ""`` true is returned.
let slen = s.len
var i = 0
var j = slen - len(suffix)
while i+j <% slen:
if s[i+j] != suffix[i]: return false
inc(i)
if suffix[i] == '\0': return true
proc cmpIgnoreStyle*(a, b: cstring): int {.noSideEffect,
rtl, extern: "csuCmpIgnoreStyle".} =
## Compares two strings normalized (i.e. case and
## underscores do not matter). Returns:
##
## | 0 iff a == b
## | < 0 iff a < b
## | > 0 iff a > b
var i = 0
var j = 0
while true:
while a[i] == '_': inc(i)
while b[j] == '_': inc(j) # BUGFIX: typo
var aa = toLowerAscii(a[i])
var bb = toLowerAscii(b[j])
result = ord(aa) - ord(bb)
if result != 0 or aa == '\0': break
inc(i)
inc(j)
proc cmpIgnoreCase*(a, b: cstring): int {.noSideEffect,
rtl, extern: "csuCmpIgnoreCase".} =
## Compares two strings in a case insensitive manner. Returns:
##
## | 0 iff a == b
## | < 0 iff a < b
## | > 0 iff a > b
var i = 0
while true:
var aa = toLowerAscii(a[i])
var bb = toLowerAscii(b[i])
result = ord(aa) - ord(bb)
if result != 0 or aa == '\0': break
inc(i)

View file

@ -923,8 +923,14 @@ proc parseChunks(client: HttpClient | AsyncHttpClient): Future[void]
if chunkSize <= 0: if chunkSize <= 0:
discard await recvFull(client, 2, client.timeout, false) # Skip \c\L discard await recvFull(client, 2, client.timeout, false) # Skip \c\L
break break
discard await recvFull(client, chunkSize, client.timeout, true) var bytesRead = await recvFull(client, chunkSize, client.timeout, true)
discard await recvFull(client, 2, client.timeout, false) # Skip \c\L if bytesRead != chunkSize:
httpError("Server terminated connection prematurely")
bytesRead = await recvFull(client, 2, client.timeout, false) # Skip \c\L
if bytesRead != 2:
httpError("Server terminated connection prematurely")
# Trailer headers will only be sent if the request specifies that we want # Trailer headers will only be sent if the request specifies that we want
# them: http://tools.ietf.org/html/rfc2616#section-3.6.1 # them: http://tools.ietf.org/html/rfc2616#section-3.6.1
@ -965,7 +971,7 @@ proc parseBody(client: HttpClient | AsyncHttpClient,
if headers.getOrDefault"Connection" == "close" or httpVersion == "1.0": if headers.getOrDefault"Connection" == "close" or httpVersion == "1.0":
while true: while true:
let recvLen = await client.recvFull(4000, client.timeout, true) let recvLen = await client.recvFull(4000, client.timeout, true)
if recvLen == 0: if recvLen != 4000:
client.close() client.close()
break break

View file

@ -277,15 +277,16 @@ proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
var events = {Event.Timer} var events = {Event.Timer}
var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP) var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
epv.data.u64 = fdi.uint epv.data.u64 = fdi.uint
if oneshot: if oneshot:
new_ts.it_interval.tv_sec = 0.Time new_ts.it_interval.tv_sec = posix.Time(0)
new_ts.it_interval.tv_nsec = 0 new_ts.it_interval.tv_nsec = 0
new_ts.it_value.tv_sec = (timeout div 1_000).Time new_ts.it_value.tv_sec = posix.Time(timeout div 1_000)
new_ts.it_value.tv_nsec = (timeout %% 1_000) * 1_000_000 new_ts.it_value.tv_nsec = (timeout %% 1_000) * 1_000_000
incl(events, Event.Oneshot) incl(events, Event.Oneshot)
epv.events = epv.events or EPOLLONESHOT epv.events = epv.events or EPOLLONESHOT
else: else:
new_ts.it_interval.tv_sec = (timeout div 1000).Time new_ts.it_interval.tv_sec = posix.Time(timeout div 1000)
new_ts.it_interval.tv_nsec = (timeout %% 1_000) * 1_000_000 new_ts.it_interval.tv_nsec = (timeout %% 1_000) * 1_000_000
new_ts.it_value.tv_sec = new_ts.it_interval.tv_sec new_ts.it_value.tv_sec = new_ts.it_interval.tv_sec
new_ts.it_value.tv_nsec = new_ts.it_interval.tv_nsec new_ts.it_value.tv_nsec = new_ts.it_interval.tv_nsec

View file

@ -452,10 +452,10 @@ proc selectInto*[T](s: Selector[T], timeout: int,
if timeout != -1: if timeout != -1:
if timeout >= 1000: if timeout >= 1000:
tv.tv_sec = (timeout div 1_000).Time tv.tv_sec = posix.Time(timeout div 1_000)
tv.tv_nsec = (timeout %% 1_000) * 1_000_000 tv.tv_nsec = (timeout %% 1_000) * 1_000_000
else: else:
tv.tv_sec = 0.Time tv.tv_sec = posix.Time(0)
tv.tv_nsec = timeout * 1_000_000 tv.tv_nsec = timeout * 1_000_000
else: else:
ptv = nil ptv = nil

View file

@ -107,9 +107,14 @@ var
proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): string = proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): string =
## Format a log message using the ``frmt`` format string, ``level`` and varargs. ## Format a log message using the ``frmt`` format string, ``level`` and varargs.
## See the module documentation for the format string syntax. ## See the module documentation for the format string syntax.
const nilString = "nil"
var msgLen = 0 var msgLen = 0
for arg in args: for arg in args:
msgLen += arg.len if arg.isNil:
msgLen += nilString.len
else:
msgLen += arg.len
result = newStringOfCap(frmt.len + msgLen + 20) result = newStringOfCap(frmt.len + msgLen + 20)
var i = 0 var i = 0
while i < frmt.len: while i < frmt.len:
@ -136,7 +141,10 @@ proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): str
of "levelname": result.add(LevelNames[level]) of "levelname": result.add(LevelNames[level])
else: discard else: discard
for arg in args: for arg in args:
result.add(arg) if arg.isNil:
result.add(nilString)
else:
result.add(arg)
method log*(logger: Logger, level: Level, args: varargs[string, `$`]) {. method log*(logger: Logger, level: Level, args: varargs[string, `$`]) {.
raises: [Exception], gcsafe, raises: [Exception], gcsafe,
@ -361,3 +369,6 @@ when not defined(testing) and isMainModule:
addHandler(L) addHandler(L)
for i in 0 .. 25: for i in 0 .. 25:
info("hello", i) info("hello", i)
var nilString: string
info "hello ", nilString

View file

@ -291,6 +291,8 @@ when not defined(JS):
## echo fmod(-2.5, 0.3) ## -0.1 ## echo fmod(-2.5, 0.3) ## -0.1
else: else:
proc trunc*(x: float32): float32 {.importc: "Math.trunc", nodecl.}
proc trunc*(x: float64): float64 {.importc: "Math.trunc", nodecl.}
proc floor*(x: float32): float32 {.importc: "Math.floor", nodecl.} proc floor*(x: float32): float32 {.importc: "Math.floor", nodecl.}
proc floor*(x: float64): float64 {.importc: "Math.floor", nodecl.} proc floor*(x: float64): float64 {.importc: "Math.floor", nodecl.}
proc ceil*(x: float32): float32 {.importc: "Math.ceil", nodecl.} proc ceil*(x: float32): float32 {.importc: "Math.ceil", nodecl.}
@ -349,15 +351,19 @@ proc round*[T: float32|float64](x: T, places: int = 0): T =
result = round0(x*mult)/mult result = round0(x*mult)/mult
when not defined(JS): when not defined(JS):
proc frexp*(x: float32, exponent: var int): float32 {. proc c_frexp*(x: float32, exponent: var int32): float32 {.
importc: "frexp", header: "<math.h>".} importc: "frexp", header: "<math.h>".}
proc frexp*(x: float64, exponent: var int): float64 {. proc c_frexp*(x: float64, exponent: var int32): float64 {.
importc: "frexp", header: "<math.h>".} importc: "frexp", header: "<math.h>".}
proc frexp*[T, U](x: T, exponent: var U): T =
## Split a number into mantissa and exponent. ## Split a number into mantissa and exponent.
## `frexp` calculates the mantissa m (a float greater than or equal to 0.5 ## `frexp` calculates the mantissa m (a float greater than or equal to 0.5
## and less than 1) and the integer value n such that `x` (the original ## and less than 1) and the integer value n such that `x` (the original
## float value) equals m * 2**n. frexp stores n in `exponent` and returns ## float value) equals m * 2**n. frexp stores n in `exponent` and returns
## m. ## m.
var exp: int32
result = c_frexp(x, exp)
exponent = exp
else: else:
proc frexp*[T: float32|float64](x: T, exponent: var int): T = proc frexp*[T: float32|float64](x: T, exponent: var int): T =
if x == 0.0: if x == 0.0:
@ -366,9 +372,14 @@ else:
elif x < 0.0: elif x < 0.0:
result = -frexp(-x, exponent) result = -frexp(-x, exponent)
else: else:
var ex = floor(log2(x)) var ex = trunc(log2(x))
exponent = round(ex) exponent = int(ex)
result = x / pow(2.0, ex) result = x / pow(2.0, ex)
if abs(result) >= 1:
inc(exponent)
result = result / 2
if exponent == 1024 and result == 0.0:
result = 0.99999999999999988898
proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] = proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] =
## Breaks `x` into an integral and a fractional part. ## Breaks `x` into an integral and a fractional part.

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