Merge branch 'devel' into pr-remove-encode-overload

This commit is contained in:
AmjadHD
2022-10-01 20:36:15 +01:00
405 changed files with 4175 additions and 4202 deletions

View File

@@ -7,11 +7,39 @@
- `addr` is now available for all addressable locations,
`unsafeAddr` is now deprecated and an alias for `addr`.
- `io`, `assertions`, `formatfloat`, and `` dollars.`$` `` for objects are about to move out of the `system` module. You may instead import `std/syncio`, `std/assertions`, `std/formatfloat` and `std/objectdollar`.
The `-d:nimPreviewSlimSystem` option makes these imports required.
- Certain definitions from the default `system` module have been moved to
the following new modules:
- `std/syncio`
- `std/assertions`
- `std/formatfloat`
- `std/objectdollar`
In the future, these definitions will be removed from the `system` module,
and their respective modules will have to be imported to use them.
Currently, to make these imports required, the `-d:nimPreviewSlimSystem` option
may be used.
- Enabling `-d:nimPreviewSlimSystem` also removes the following deprecated
symbols in the `system` module:
- Aliases with `Error` suffix to exception types that have a `Defect` suffix
(see [exceptions](https://nim-lang.org/docs/exceptions.html)):
`ArithmeticError`, `DivByZeroError`, `OverflowError`,
`AccessViolationError`, `AssertionError`, `OutOfMemError`, `IndexError`,
`FieldError`, `RangeError`, `StackOverflowError`, `ReraiseError`,
`ObjectAssignmentError`, `ObjectConversionError`, `FloatingPointError`,
`FloatOverflowError`, `FloatUnderflowError`, `FloatInexactError`,
`DeadThreadError`, `NilAccessError`
- `addQuitProc`, replaced by `exitprocs.addExitProc`
- Legacy unsigned conversion operations: `ze`, `ze64`, `toU8`, `toU16`, `toU32`
- `TaintedString`, formerly a distinct alias to `string`
- `PInt32`, `PInt64`, `PFloat32`, `PFloat64`, aliases to
`ptr int32`, `ptr int64`, `ptr float32`, `ptr float64`
- The `gc:v2` option is removed.
- The `mainmodule` and `m` options are removed.
- The `threads:on` option is now the default.
- Optional parameters in combination with `: body` syntax (RFC #405) are now opt-in via
@@ -42,13 +70,19 @@
- Removed two type pragma syntaxes deprecated since 0.20, namely
`type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`.
- [Overloadable enums](https://nim-lang.github.io/Nim/manual_experimental.html#overloadable-enum-value-names)
- [Overloadable enums](https://nim-lang.github.io/Nim/manual.html#overloadable-enum-value-names) and Unicode Operators
are no longer experimental.
- Removed the `nimIncrSeqV3` define.
- Static linking against OpenSSL versions below 1.1, previously done by
setting `-d:openssl10`, is no longer supported.
- `macros.getImpl` for `const` symbols now returns the full definition node
(as `nnkConstDef`) rather than the AST of the constant value.
- ORC is now the default memory management strategy. Use
`--mm:refc` for a transition period.
## Standard library additions and changes
@@ -65,6 +99,7 @@
- `strutils.find` now uses and defaults to `last = -1` for whole string searches,
making limiting it to just the first char (`last = 0`) valid.
- `random.rand` now works with `Ordinal`s.
- Undeprecated `os.isvalidfilename`.
- `std/oids` now uses `int64` to store time internally (before it was int32), the length of
the string form of `Oid` changes from 24 to 32.

124
compiler/aliasanalysis.nim Normal file
View File

@@ -0,0 +1,124 @@
import ast
import std / assertions
const
PathKinds0* = {nkDotExpr, nkCheckedFieldExpr,
nkBracketExpr, nkDerefExpr, nkHiddenDeref,
nkAddr, nkHiddenAddr,
nkObjDownConv, nkObjUpConv}
PathKinds1* = {nkHiddenStdConv, nkHiddenSubConv}
proc skipConvDfa*(n: PNode): PNode =
result = n
while true:
case result.kind
of nkObjDownConv, nkObjUpConv:
result = result[0]
of PathKinds1:
result = result[1]
else: break
proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
var n = orig
while true:
case n.kind
of PathKinds0 - {nkHiddenDeref, nkDerefExpr}:
n = n[0]
of PathKinds1:
n = n[1]
of nkHiddenDeref, nkDerefExpr:
# We "own" sinkparam[].loc but not ourVar[].location as it is a nasty
# pointer indirection.
# bug #14159, we cannot reason about sinkParam[].location as it can
# still be shared for tyRef.
n = n[0]
return n.kind == nkSym and n.sym.owner == owner and
(n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned})
else: break
# XXX Allow closure deref operations here if we know
# the owner controlled the closure allocation?
result = n.kind == nkSym and n.sym.owner == owner and
{sfGlobal, sfThread, sfCursor} * n.sym.flags == {} and
(n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar)
# Note: There is a different move analyzer possible that checks for
# consume(param.key); param.key = newValue for all paths. Then code like
#
# let splited = split(move self.root, x)
# self.root = merge(splited.lower, splited.greater)
#
# could be written without the ``move self.root``. However, this would be
# wrong! Then the write barrier for the ``self.root`` assignment would
# free the old data and all is lost! Lesson: Don't be too smart, trust the
# lower level C++ optimizer to specialize this code.
type AliasKind* = enum
yes, no, maybe
proc aliases*(obj, field: PNode): AliasKind =
# obj -> field:
# x -> x: true
# x -> x.f: true
# x.f -> x: false
# x.f -> x.f: true
# x.f -> x.v: false
# x -> x[0]: true
# x[0] -> x: false
# x[0] -> x[0]: true
# x[0] -> x[1]: false
# x -> x[i]: true
# x[i] -> x: false
# x[i] -> x[i]: maybe; Further analysis could make this return true when i is a runtime-constant
# x[i] -> x[j]: maybe; also returns maybe if only one of i or j is a compiletime-constant
template collectImportantNodes(result, n) =
var result: seq[PNode]
var n = n
while true:
case n.kind
of PathKinds0 - {nkDotExpr, nkBracketExpr}:
n = n[0]
of PathKinds1:
n = n[1]
of nkDotExpr, nkBracketExpr:
result.add n
n = n[0]
of nkSym:
result.add n
break
else: return no
collectImportantNodes(objImportantNodes, obj)
collectImportantNodes(fieldImportantNodes, field)
# If field is less nested than obj, then it cannot be part of/aliased by obj
if fieldImportantNodes.len < objImportantNodes.len: return no
result = yes
for i in 1..objImportantNodes.len:
# We compare the nodes leading to the location of obj and field
# with each other.
# We continue until they diverge, in which case we return no, or
# until we reach the location of obj, in which case we do not need
# to look further, since field must be part of/aliased by obj now.
# If we encounter an element access using an index which is a runtime value,
# we simply return maybe instead of yes; should further nodes not diverge.
let currFieldPath = fieldImportantNodes[^i]
let currObjPath = objImportantNodes[^i]
if currFieldPath.kind != currObjPath.kind:
return no
case currFieldPath.kind
of nkSym:
if currFieldPath.sym != currObjPath.sym: return no
of nkDotExpr:
if currFieldPath[1].sym != currObjPath[1].sym: return no
of nkBracketExpr:
if currFieldPath[1].kind in nkLiterals and currObjPath[1].kind in nkLiterals:
if currFieldPath[1].intVal != currObjPath[1].intVal:
return no
else:
result = maybe
else: assert false # unreachable

View File

@@ -508,7 +508,8 @@ type
# the flag is applied to proc default values and to calls
nfExecuteOnReload # A top-level statement that will be executed during reloads
nfLastRead # this node is a last read
nfFirstWrite# this node is a first write
nfFirstWrite # this node is a first write
nfFirstWrite2 # alternative first write implementation
nfHasComment # node has a comment
TNodeFlags* = set[TNodeFlag]
@@ -745,7 +746,7 @@ const
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
mConStrStr, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
mInSet, mRepr, mOpenArrayToSeq}
generatedMagics* = {mNone, mIsolate, mFinished, mOpenArrayToSeq}
## magics that are generated as normal procs in the backend
@@ -1075,7 +1076,8 @@ const
nfDotSetter, nfDotField,
nfIsRef, nfIsPtr, nfPreventCg, nfLL,
nfFromTemplate, nfDefaultRefsParam,
nfExecuteOnReload, nfLastRead, nfFirstWrite}
nfExecuteOnReload, nfLastRead,
nfFirstWrite, nfFirstWrite2}
namePos* = 0
patternPos* = 1 # empty except for term rewriting macros
genericParamsPos* = 2
@@ -1233,6 +1235,25 @@ proc getDeclPragma*(n: PNode): PNode =
if result != nil:
assert result.kind == nkPragma, $(result.kind, n.kind)
proc extractPragma*(s: PSym): PNode =
## gets the pragma node of routine/type/var/let/const symbol `s`
if s.kind in routineKinds:
result = s.ast[pragmasPos]
elif s.kind in {skType, skVar, skLet, skConst}:
if s.ast != nil and s.ast.len > 0:
if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1:
# s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma]
result = s.ast[0][1]
assert result == nil or result.kind == nkPragma
proc skipPragmaExpr*(n: PNode): PNode =
## if pragma expr, give the node the pragmas are applied to,
## otherwise give node itself
if n.kind == nkPragmaExpr:
result = n[0]
else:
result = n
when defined(useNodeIds):
const nodeIdToDebug* = -1 # 2322968
var gNodeId: int
@@ -1321,7 +1342,7 @@ proc newSym*(symKind: TSymKind, name: PIdent, id: ItemId, owner: PSym,
proc astdef*(s: PSym): PNode =
# get only the definition (initializer) portion of the ast
if s.ast != nil and s.ast.kind == nkIdentDefs:
if s.ast != nil and s.ast.kind in {nkIdentDefs, nkConstDef}:
s.ast[2]
else:
s.ast
@@ -1504,7 +1525,7 @@ proc mergeLoc(a: var TLoc, b: TLoc) =
if a.storage == low(typeof(a.storage)): a.storage = b.storage
a.flags.incl b.flags
if a.lode == nil: a.lode = b.lode
if a.r == nil: a.r = b.r
if a.r == "": a.r = b.r
proc newSons*(father: Indexable, length: int) =
setLen(father.sons, length)

View File

@@ -12,9 +12,11 @@
# the data structures here are used in various places of the compiler.
import
ast, hashes, intsets, strutils, options, lineinfos, ropes, idents, rodutils,
ast, hashes, intsets, options, lineinfos, ropes, idents, rodutils,
msgs
import strutils except addf
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -258,7 +260,7 @@ proc makeYamlString*(s: string): Rope =
# this could trigger InternalError(111). See the ropes module for
# further information.
const MaxLineLength = 64
result = nil
result = ""
var res = "\""
for i in 0..<s.len:
if (i + 1) mod MaxLineLength == 0:
@@ -274,9 +276,9 @@ proc flagsToStr[T](flags: set[T]): Rope =
if flags == {}:
result = rope("[]")
else:
result = nil
result = ""
for x in items(flags):
if result != nil: result.add(", ")
if result != "": result.add(", ")
result.add(makeYamlString($x))
result = "[" & result & "]"

View File

@@ -77,12 +77,12 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
callee, params: Rope) =
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
genLineDir(p, ri)
var pl = callee & ~"(" & params
var pl = callee & "(" & params
# getUniqueType() is too expensive here:
var typ = skipTypes(ri[0].typ, abstractInst)
if typ[0] != nil:
if isInvalidReturnType(p.config, typ):
if params != nil: pl.add(~", ")
if params.len != 0: pl.add(", ")
# beware of 'result = p(result)'. We may need to allocate a temporary:
if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
# Great, we can use 'd':
@@ -91,18 +91,18 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
# reset before pass as 'result' var:
discard "resetLoc(p, d)"
pl.add(addrLoc(p.config, d))
pl.add(~");$n")
pl.add(");\n")
line(p, cpsStmts, pl)
else:
var tmp: TLoc
getTemp(p, typ[0], tmp, needsInit=true)
pl.add(addrLoc(p.config, tmp))
pl.add(~");$n")
pl.add(");\n")
line(p, cpsStmts, pl)
genAssignment(p, d, tmp, {}) # no need for deep copying
if canRaise: raiseExit(p)
else:
pl.add(~")")
pl.add(")")
if p.module.compileToCpp:
if lfSingleUse in d.flags:
# do not generate spurious temporaries for C++! For C we're better off
@@ -140,7 +140,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
if canRaise: raiseExit(p)
genAssignment(p, d, tmp, {})
else:
pl.add(~");$n")
pl.add(");\n")
line(p, cpsStmts, pl)
if canRaise: raiseExit(p)
@@ -173,8 +173,10 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType): (Rope,
result = ("($3*)(($1)+($2))" % [rdLoc(a), rdLoc(b), dest],
lengthExpr)
else:
var lit = newRopeAppender()
intLiteral(first, lit)
result = ("($4*)($1)+(($2)-($3))" %
[rdLoc(a), rdLoc(b), intLiteral(first), dest],
[rdLoc(a), rdLoc(b), lit, dest],
lengthExpr)
of tyOpenArray, tyVarargs:
if reifiedOpenArray(q[1]):
@@ -200,7 +202,7 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType): (Rope,
else:
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
var q = skipConv(n)
var skipped = false
while q.kind == nkStmtListExpr and q.len > 0:
@@ -215,7 +217,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
genStmts(p, q[i])
q = q.lastSon
let (x, y) = genOpenArraySlice(p, q, formalType, n.typ[0])
result = x & ", " & y
result.add x & ", " & y
else:
var a: TLoc
initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n, a)
@@ -223,11 +225,11 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
of tyOpenArray, tyVarargs:
if reifiedOpenArray(n):
if a.t.kind in {tyVar, tyLent}:
result = "$1->Field0, $1->Field1" % [rdLoc(a)]
result.add "$1->Field0, $1->Field1" % [rdLoc(a)]
else:
result = "$1.Field0, $1.Field1" % [rdLoc(a)]
result.add "$1.Field0, $1.Field1" % [rdLoc(a)]
else:
result = "$1, $1Len_0" % [rdLoc(a)]
result.add "$1, $1Len_0" % [rdLoc(a)]
of tyString, tySequence:
let ntyp = skipTypes(n.typ, abstractInst)
if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and
@@ -236,19 +238,19 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
var t: TLoc
t.r = "(*$1)" % [a.rdLoc]
result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)]
result.add "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)]
else:
result = "$1$3, $2" % [a.rdLoc, lenExpr(p, a), dataField(p)]
result.add "$1$3, $2" % [a.rdLoc, lenExpr(p, a), dataField(p)]
of tyArray:
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
of tyPtr, tyRef:
case lastSon(a.t).kind
of tyString, tySequence:
var t: TLoc
t.r = "(*$1)" % [a.rdLoc]
result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)]
result.add "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)]
of tyArray:
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))]
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))]
else:
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
else: internalError(p.config, "openArrayLoc: " & typeToString(a.t))
@@ -268,24 +270,24 @@ proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc =
getTemp(p, a.lode.typ, result, needsInit=false)
genAssignment(p, result, a, {})
proc genArgStringToCString(p: BProc, n: PNode, needsTmp: bool): Rope {.inline.} =
proc genArgStringToCString(p: BProc, n: PNode; result: var Rope; needsTmp: bool) {.inline.} =
var a: TLoc
initLocExpr(p, n[0], a)
ropecg(p.module, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc])
appcg(p.module, result, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc])
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode, needsTmp = false): Rope =
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; needsTmp = false) =
var a: TLoc
if n.kind == nkStringToCString:
result = genArgStringToCString(p, n, needsTmp)
genArgStringToCString(p, n, result, needsTmp)
elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
var n = if n.kind != nkHiddenAddr: n else: n[0]
result = openArrayLoc(p, param.typ, n)
openArrayLoc(p, param.typ, n, result)
elif ccgIntroducedPtr(p.config, param, call[0].typ[0]):
initLocExpr(p, n, a)
if n.kind in {nkCharLit..nkNilLit}:
result = addrLoc(p.config, literalsNeedsTmp(p, a))
addAddrLoc(p.config, literalsNeedsTmp(p, a), result)
else:
result = addrLoc(p.config, withTmpIfNeeded(p, a, needsTmp))
addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result)
elif p.module.compileToCpp and param.typ.kind in {tyVar} and
n.kind == nkHiddenAddr:
initLocExprSingleUse(p, n[0], a)
@@ -295,23 +297,23 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode, needsTmp = false): Rop
if callee.kind == nkSym and
{sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
result = addrLoc(p.config, a)
addAddrLoc(p.config, a, result)
else:
result = rdLoc(a)
addRdLoc(a, result)
else:
initLocExprSingleUse(p, n, a)
result = rdLoc(withTmpIfNeeded(p, a, needsTmp))
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
#assert result != nil
proc genArgNoParam(p: BProc, n: PNode, needsTmp = false): Rope =
proc genArgNoParam(p: BProc, n: PNode; result: var Rope; needsTmp = false) =
var a: TLoc
if n.kind == nkStringToCString:
result = genArgStringToCString(p, n, needsTmp)
genArgStringToCString(p, n, result, needsTmp)
else:
initLocExprSingleUse(p, n, a)
result = rdLoc(withTmpIfNeeded(p, a, needsTmp))
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
from dfa import aliases, AliasKind
import aliasanalysis
proc potentialAlias(n: PNode, potentialWrites: seq[PNode]): bool =
for p in potentialWrites:
@@ -365,7 +367,7 @@ proc getPotentialReads(n: PNode; result: var seq[PNode]) =
for s in n:
getPotentialReads(s, result)
proc genParams(p: BProc, ri: PNode, typ: PType): Rope =
proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
# We must generate temporaries in cases like #14396
# to keep the strict Left-To-Right evaluation
var needTmp = newSeq[bool](ri.len - 1)
@@ -385,16 +387,21 @@ proc genParams(p: BProc, ri: PNode, typ: PType): Rope =
# Optimization: don't use a temp, if we would only take the address anyway
needTmp[i - 1] = false
var oldLen = result.len
for i in 1..<ri.len:
if i < typ.len:
assert(typ.n[i].kind == nkSym)
let paramType = typ.n[i]
if not paramType.typ.isCompileTimeOnly:
if result != nil: result.add(~", ")
result.add(genArg(p, ri[i], paramType.sym, ri, needTmp[i-1]))
if oldLen != result.len:
result.add(", ")
oldLen = result.len
genArg(p, ri[i], paramType.sym, ri, result, needTmp[i-1])
else:
if result != nil: result.add(~", ")
result.add(genArgNoParam(p, ri[i], needTmp[i-1]))
if oldLen != result.len:
result.add(", ")
oldLen = result.len
genArgNoParam(p, ri[i], result, needTmp[i-1])
proc addActualSuffixForHCR(res: var Rope, module: PSym, sym: PSym) =
if sym.flags * {sfImportc, sfNonReloadable} == {} and sym.loc.k == locProc and
@@ -410,7 +417,8 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
assert(typ.kind == tyProc)
assert(typ.len == typ.n.len)
var params = genParams(p, ri, typ)
var params = newRopeAppender()
genParams(p, ri, typ, params)
var callee = rdLoc(op)
if p.hcrOn and ri[0].kind == nkSym:
@@ -420,7 +428,7 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
proc addComma(r: Rope): Rope =
if r == nil: r else: r & ~", "
if r.len == 0: r else: r & ", "
const PatProc = "$1.ClE_0? $1.ClP_0($3$1.ClE_0):(($4)($1.ClP_0))($2)"
const PatIter = "$1.ClP_0($3$1.ClE_0)" # we know the env exists
@@ -433,7 +441,8 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
assert(typ.kind == tyProc)
assert(typ.len == typ.n.len)
var pl = genParams(p, ri, typ)
var pl = newRopeAppender()
genParams(p, ri, typ, pl)
template genCallPattern {.dirty.} =
if tfIterator in typ.flags:
@@ -445,7 +454,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
if typ[0] != nil:
if isInvalidReturnType(p.config, typ):
if ri.len > 1: pl.add(~", ")
if ri.len > 1: pl.add(", ")
# beware of 'result = p(result)'. We may need to allocate a temporary:
if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
# Great, we can use 'd':
@@ -491,24 +500,30 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
genCallPattern()
if canRaise: raiseExit(p)
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope;
argsCounter: var int) =
if i < typ.len:
# 'var T' is 'T&' in C++. This means we ignore the request of
# any nkHiddenAddr when it's a 'var T'.
let paramType = typ.n[i]
assert(paramType.kind == nkSym)
if paramType.typ.isCompileTimeOnly:
result = nil
discard
elif typ[i].kind in {tyVar} and ri[i].kind == nkHiddenAddr:
result = genArgNoParam(p, ri[i][0])
if argsCounter > 0: result.add ", "
genArgNoParam(p, ri[i][0], result)
inc argsCounter
else:
result = genArgNoParam(p, ri[i]) #, typ.n[i].sym)
if argsCounter > 0: result.add ", "
genArgNoParam(p, ri[i], result) #, typ.n[i].sym)
inc argsCounter
else:
if tfVarargs notin typ.flags:
localError(p.config, ri.info, "wrong argument count")
result = nil
else:
result = genArgNoParam(p, ri[i])
if argsCounter > 0: result.add ", "
genArgNoParam(p, ri[i], result)
inc argsCounter
discard """
Dot call syntax in C++
@@ -565,7 +580,7 @@ proc skipAddrDeref(node: PNode): PNode =
else:
result = node
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope) =
# for better or worse c2nim translates the 'this' argument to a 'var T'.
# However manual wrappers may also use 'ptr T'. In any case we support both
# for convenience.
@@ -579,75 +594,72 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
if t.kind in {tyVar}:
let x = if ri.kind == nkHiddenAddr: ri[0] else: ri
if x.typ.kind == tyPtr:
result = genArgNoParam(p, x)
genArgNoParam(p, x, result)
result.add("->")
elif x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].typ.kind == tyPtr:
result = genArgNoParam(p, x[0])
genArgNoParam(p, x[0], result)
result.add("->")
else:
result = genArgNoParam(p, x)
genArgNoParam(p, x, result)
result.add(".")
elif t.kind == tyPtr:
if ri.kind in {nkAddr, nkHiddenAddr}:
result = genArgNoParam(p, ri[0])
genArgNoParam(p, ri[0], result)
result.add(".")
else:
result = genArgNoParam(p, ri)
genArgNoParam(p, ri, result)
result.add("->")
else:
ri = skipAddrDeref(ri)
if ri.kind in {nkAddr, nkHiddenAddr}: ri = ri[0]
result = genArgNoParam(p, ri) #, typ.n[i].sym)
genArgNoParam(p, ri, result) #, typ.n[i].sym)
result.add(".")
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope =
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Rope) =
var i = 0
var j = 1
while i < pat.len:
case pat[i]
of '@':
var first = true
var argsCounter = 0
for k in j..<ri.len:
let arg = genOtherArg(p, ri, k, typ)
if arg.len > 0:
if not first:
result.add(~", ")
first = false
result.add arg
genOtherArg(p, ri, k, typ, result, argsCounter)
inc i
of '#':
if i+1 < pat.len and pat[i+1] in {'+', '@'}:
let ri = ri[j]
if ri.kind in nkCallKinds:
let typ = skipTypes(ri[0].typ, abstractInst)
if pat[i+1] == '+': result.add genArgNoParam(p, ri[0])
result.add(~"(")
if pat[i+1] == '+': genArgNoParam(p, ri[0], result)
result.add("(")
if 1 < ri.len:
result.add genOtherArg(p, ri, 1, typ)
var argsCounterB = 0
genOtherArg(p, ri, 1, typ, result, argsCounterB)
for k in j+1..<ri.len:
result.add(~", ")
result.add genOtherArg(p, ri, k, typ)
result.add(~")")
var argsCounterB = 0
genOtherArg(p, ri, k, typ, result, argsCounterB)
result.add(")")
else:
localError(p.config, ri.info, "call expression expected for C++ pattern")
inc i
elif i+1 < pat.len and pat[i+1] == '.':
result.add genThisArg(p, ri, j, typ)
genThisArg(p, ri, j, typ, result)
inc i
elif i+1 < pat.len and pat[i+1] == '[':
var arg = ri[j].skipAddrDeref
while arg.kind in {nkAddr, nkHiddenAddr, nkObjDownConv}: arg = arg[0]
result.add genArgNoParam(p, arg)
genArgNoParam(p, arg, result)
#result.add debugTree(arg, 0, 10)
else:
result.add genOtherArg(p, ri, j, typ)
var argsCounter = 0
genOtherArg(p, ri, j, typ, result, argsCounter)
inc j
inc i
of '\'':
var idx, stars: int
if scanCppGenericSlot(pat, i, idx, stars):
var t = resolveStarsInCppType(typ, idx, stars)
if t == nil: result.add(~"void")
if t == nil: result.add("void")
else: result.add(getTypeDesc(p.module, t))
else:
let start = i
@@ -665,10 +677,11 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
assert(typ.kind == tyProc)
assert(typ.len == typ.n.len)
# don't call '$' here for efficiency:
let pat = ri[0].sym.loc.r.data
let pat = $ri[0].sym.loc.r
internalAssert p.config, pat.len > 0
if pat.contains({'#', '(', '@', '\''}):
var pl = genPatternCall(p, ri, pat, typ)
var pl = newRopeAppender()
genPatternCall(p, ri, pat, typ, pl)
# simpler version of 'fixupCall' that works with the pl+params combination:
var typ = skipTypes(ri[0].typ, abstractInst)
if typ[0] != nil:
@@ -687,80 +700,79 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
else:
pl.add(~";$n")
pl.add(";\n")
line(p, cpsStmts, pl)
else:
var pl: Rope = nil
#var param = typ.n[1].sym
var pl = newRopeAppender()
var argsCounter = 0
if 1 < ri.len:
pl.add(genThisArg(p, ri, 1, typ))
genThisArg(p, ri, 1, typ, pl)
pl.add(op.r)
var params: Rope
var params = newRopeAppender()
for i in 2..<ri.len:
if params != nil: params.add(~", ")
assert(typ.len == typ.n.len)
params.add(genOtherArg(p, ri, i, typ))
genOtherArg(p, ri, i, typ, params, argsCounter)
fixupCall(p, le, ri, d, pl, params)
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
# generates a crappy ObjC call
var op: TLoc
initLocExpr(p, ri[0], op)
var pl = ~"["
var pl = "["
# getUniqueType() is too expensive here:
var typ = skipTypes(ri[0].typ, abstractInst)
assert(typ.kind == tyProc)
assert(typ.len == typ.n.len)
# don't call '$' here for efficiency:
let pat = ri[0].sym.loc.r.data
let pat = $ri[0].sym.loc.r
internalAssert p.config, pat.len > 0
var start = 3
if ' ' in pat:
start = 1
pl.add(op.r)
if ri.len > 1:
pl.add(~": ")
pl.add(genArg(p, ri[1], typ.n[1].sym, ri))
pl.add(": ")
genArg(p, ri[1], typ.n[1].sym, ri, pl)
start = 2
else:
if ri.len > 1:
pl.add(genArg(p, ri[1], typ.n[1].sym, ri))
pl.add(~" ")
genArg(p, ri[1], typ.n[1].sym, ri, pl)
pl.add(" ")
pl.add(op.r)
if ri.len > 2:
pl.add(~": ")
pl.add(genArg(p, ri[2], typ.n[2].sym, ri))
pl.add(": ")
genArg(p, ri[2], typ.n[2].sym, ri, pl)
for i in start..<ri.len:
assert(typ.len == typ.n.len)
if i >= typ.len:
internalError(p.config, ri.info, "varargs for objective C method?")
assert(typ.n[i].kind == nkSym)
var param = typ.n[i].sym
pl.add(~" ")
pl.add(" ")
pl.add(param.name.s)
pl.add(~": ")
pl.add(genArg(p, ri[i], param, ri))
pl.add(": ")
genArg(p, ri[i], param, ri, pl)
if typ[0] != nil:
if isInvalidReturnType(p.config, typ):
if ri.len > 1: pl.add(~" ")
if ri.len > 1: pl.add(" ")
# beware of 'result = p(result)'. We always allocate a temporary:
if d.k in {locTemp, locNone}:
# We already got a temp. Great, special case it:
if d.k == locNone: getTemp(p, typ[0], d, needsInit=true)
pl.add(~"Result: ")
pl.add("Result: ")
pl.add(addrLoc(p.config, d))
pl.add(~"];$n")
pl.add("];\n")
line(p, cpsStmts, pl)
else:
var tmp: TLoc
getTemp(p, typ[0], tmp, needsInit=true)
pl.add(addrLoc(p.config, tmp))
pl.add(~"];$n")
pl.add("];\n")
line(p, cpsStmts, pl)
genAssignment(p, d, tmp, {}) # no need for deep copying
else:
pl.add(~"]")
pl.add("]")
if d.k == locNone: getTemp(p, typ[0], d)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc
@@ -768,7 +780,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
else:
pl.add(~"];$n")
pl.add("];\n")
line(p, cpsStmts, pl)
proc notYetAlive(n: PNode): bool {.inline.} =

File diff suppressed because it is too large Load Diff

View File

@@ -21,7 +21,7 @@ template detectVersion(field, corename) =
if core == nil or core.kind != skConst:
m.g.field = 1
else:
m.g.field = toInt(ast.getInt(core.ast))
m.g.field = toInt(ast.getInt(core.astdef))
result = m.g.field
proc detectStrVersion(m: BModule): int =
@@ -32,82 +32,87 @@ proc detectSeqVersion(m: BModule): int =
# ----- Version 1: GC'ed strings and seqs --------------------------------
proc genStringLiteralDataOnlyV1(m: BModule, s: string): Rope =
discard cgsym(m, "TGenericSeq")
result = getTempName(m)
m.s[cfsData].addf("STRING_LITERAL($1, $2, $3);$n",
[result, makeCString(s), rope(s.len)])
proc genStringLiteralDataOnlyV1(m: BModule, s: string; result: var Rope) =
cgsym(m, "TGenericSeq")
let tmp = getTempName(m)
result.add tmp
m.s[cfsStrData].addf("STRING_LITERAL($1, $2, $3);$n",
[tmp, makeCString(s), rope(s.len)])
proc genStringLiteralV1(m: BModule; n: PNode): Rope =
proc genStringLiteralV1(m: BModule; n: PNode; result: var Rope) =
if s.isNil:
result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", [])
appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
else:
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
if id == m.labels:
# string literal not found in the cache:
result = ropecg(m, "((#NimStringDesc*) &$1)",
[genStringLiteralDataOnlyV1(m, n.strVal)])
appcg(m, result, "((#NimStringDesc*) &", [])
genStringLiteralDataOnlyV1(m, n.strVal, result)
result.add ")"
else:
result = ropecg(m, "((#NimStringDesc*) &$1$2)",
appcg(m, result, "((#NimStringDesc*) &$1$2)",
[m.tmpBase, id])
# ------ Version 2: destructor based strings and seqs -----------------------
proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bool) =
m.s[cfsData].addf("static $4 struct {$n" &
m.s[cfsStrData].addf("static $4 struct {$n" &
" NI cap; NIM_CHAR data[$2+1];$n" &
"} $1 = { $2 | NIM_STRLIT_FLAG, $3 };$n",
[result, rope(s.len), makeCString(s),
rope(if isConst: "const" else: "")])
proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool): Rope =
proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Rope) =
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
if id == m.labels:
let pureLit = getTempName(m)
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
result = getTempName(m)
discard cgsym(m, "NimStrPayload")
discard cgsym(m, "NimStringV2")
let tmp = getTempName(m)
result.add tmp
cgsym(m, "NimStrPayload")
cgsym(m, "NimStringV2")
# string literal not found in the cache:
m.s[cfsData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
[result, rope(n.strVal.len), pureLit, rope(if isConst: "const" else: "")])
m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
[tmp, rope(n.strVal.len), pureLit, rope(if isConst: "const" else: "")])
else:
result = getTempName(m)
m.s[cfsData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
[result, rope(n.strVal.len), m.tmpBase & rope(id),
let tmp = getTempName(m)
result.add tmp
m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
[tmp, rope(n.strVal.len), m.tmpBase & rope(id),
rope(if isConst: "const" else: "")])
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool): Rope =
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Rope) =
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
var pureLit: Rope
if id == m.labels:
pureLit = getTempName(m)
discard cgsym(m, "NimStrPayload")
discard cgsym(m, "NimStringV2")
cgsym(m, "NimStrPayload")
cgsym(m, "NimStringV2")
# string literal not found in the cache:
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
else:
pureLit = m.tmpBase & rope(id)
result = "{$1, (NimStrPayload*)&$2}" % [rope(n.strVal.len), pureLit]
result.addf "{$1, (NimStrPayload*)&$2}", [rope(n.strVal.len), pureLit]
# ------ Version selector ---------------------------------------------------
proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
isConst: bool): Rope =
isConst: bool; result: var Rope) =
case detectStrVersion(m)
of 0, 1: result = genStringLiteralDataOnlyV1(m, s)
of 0, 1: genStringLiteralDataOnlyV1(m, s, result)
of 2:
result = getTempName(m)
genStringLiteralDataOnlyV2(m, s, result, isConst)
let tmp = getTempName(m)
genStringLiteralDataOnlyV2(m, s, tmp, isConst)
result.add tmp
else:
localError(m.config, info, "cannot determine how to produce code for string literal")
proc genNilStringLiteral(m: BModule; info: TLineInfo): Rope =
result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", [])
proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Rope) =
appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
proc genStringLiteral(m: BModule; n: PNode): Rope =
proc genStringLiteral(m: BModule; n: PNode; result: var Rope) =
case detectStrVersion(m)
of 0, 1: result = genStringLiteralV1(m, n)
of 2: result = genStringLiteralV2(m, n, isConst = true)
of 0, 1: genStringLiteralV1(m, n, result)
of 2: genStringLiteralV2(m, n, isConst = true, result)
else:
localError(m.config, n.info, "cannot determine how to produce code for string literal")

View File

@@ -24,7 +24,7 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
of nkRecCase:
if (n[0].kind != nkSym): internalError(p.config, n.info, "specializeResetN")
let disc = n[0].sym
if disc.loc.r == nil: fillObjectFields(p.module, typ)
if disc.loc.r == "": fillObjectFields(p.module, typ)
if disc.loc.t == nil:
internalError(p.config, n.info, "specializeResetN()")
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
@@ -42,7 +42,7 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
of nkSym:
let field = n.sym
if field.typ.kind == tyVoid: return
if field.loc.r == nil: fillObjectFields(p.module, typ)
if field.loc.r == "": fillObjectFields(p.module, typ)
if field.loc.t == nil:
internalError(p.config, n.info, "specializeResetN()")
specializeResetT(p, "$1.$2" % [accessor, field.loc.r], field.loc.t)

View File

@@ -15,21 +15,22 @@ const
stringCaseThreshold = 8
# above X strings a hash-switch for strings is generated
proc getTraverseProc(p: BProc, v: PSym): Rope =
proc registerTraverseProc(p: BProc, v: PSym) =
var traverseProc = ""
if p.config.selectedGC in {gcMarkAndSweep, gcHooks, gcRefc} and
optOwnedRefs notin p.config.globalOptions and
containsGarbageCollectedRef(v.loc.t):
# we register a specialized marked proc here; this has the advantage
# that it works out of the box for thread local storage then :-)
result = genTraverseProcForGlobal(p.module, v, v.info)
traverseProc = genTraverseProcForGlobal(p.module, v, v.info)
proc registerTraverseProc(p: BProc, v: PSym, traverseProc: Rope) =
if sfThread in v.flags:
appcg(p.module, p.module.preInitProc.procSec(cpsInit),
"$n\t#nimRegisterThreadLocalMarker($1);$n$n", [traverseProc])
else:
appcg(p.module, p.module.preInitProc.procSec(cpsInit),
"$n\t#nimRegisterGlobalMarker($1);$n$n", [traverseProc])
if traverseProc.len != 0 and not p.hcrOn:
if sfThread in v.flags:
appcg(p.module, p.module.preInitProc.procSec(cpsInit),
"$n\t#nimRegisterThreadLocalMarker($1);$n$n", [traverseProc])
else:
appcg(p.module, p.module.preInitProc.procSec(cpsInit),
"$n\t#nimRegisterGlobalMarker($1);$n$n", [traverseProc])
proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} =
if n.kind == nkEmpty:
@@ -54,7 +55,9 @@ proc inExceptBlockLen(p: BProc): int =
proc startBlockInternal(p: BProc): int {.discardable.} =
inc(p.labels)
result = p.blocks.len
setLen(p.blocks, result + 1)
p.blocks.add initBlock()
p.blocks[result].id = p.labels
p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16
p.blocks[result].nestedExceptStmts = p.inExceptBlockLen.int16
@@ -78,7 +81,7 @@ proc genVarTuple(p: BProc, n: PNode) =
# check only the first son
var forHcr = treatGlobalDifferentlyForHCR(p.module, n[0].sym)
let hcrCond = if forHcr: getTempName(p.module) else: nil
let hcrCond = if forHcr: getTempName(p.module) else: ""
var hcrGlobals: seq[tuple[loc: TLoc, tp: Rope]]
# determine if the tuple is constructed at top-level scope or inside of a block (if/while/block)
let isGlobalInBlock = forHcr and p.blocks.len > 2
@@ -97,13 +100,10 @@ proc genVarTuple(p: BProc, n: PNode) =
let vn = n[i]
let v = vn.sym
if sfCompileTime in v.flags: continue
var traverseProc: Rope
if sfGlobal in v.flags:
assignGlobalVar(p, vn, nil)
assignGlobalVar(p, vn, "")
genObjectInit(p, cpsInit, v.typ, v.loc, constructObj)
traverseProc = getTraverseProc(p, v)
if traverseProc != nil and not p.hcrOn:
registerTraverseProc(p, v, traverseProc)
registerTraverseProc(p, v)
else:
assignLocalVar(p, vn)
initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[^1]))
@@ -115,7 +115,7 @@ proc genVarTuple(p: BProc, n: PNode) =
field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)]
putLocIntoDest(p, v.loc, field)
if forHcr or isGlobalInBlock:
hcrGlobals.add((loc: v.loc, tp: if traverseProc == nil: ~"NULL" else: traverseProc))
hcrGlobals.add((loc: v.loc, tp: "NULL"))
if forHcr:
# end the block where the tuple gets initialized
@@ -145,12 +145,12 @@ proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
a.flags.incl(lfEnforceDeref)
expr(p, ri, a)
proc assignLabel(b: var TBlock): Rope {.inline.} =
proc assignLabel(b: var TBlock; result: var Rope) {.inline.} =
b.label = "LA" & b.id.rope
result = b.label
result.add b.label
proc blockBody(b: var TBlock): Rope =
result = b.sections[cpsLocals]
proc blockBody(b: var TBlock; result: var Rope) =
result.add b.sections[cpsLocals]
if b.frameLen > 0:
result.addf("FR_.len+=$1;$n", [b.frameLen.rope])
result.add(b.sections[cpsInit])
@@ -159,7 +159,7 @@ proc blockBody(b: var TBlock): Rope =
proc endBlock(p: BProc, blockEnd: Rope) =
let topBlock = p.blocks.len-1
# the block is merged into the parent block
p.blocks[topBlock-1].sections[cpsStmts].add(p.blocks[topBlock].blockBody)
p.blocks[topBlock].blockBody(p.blocks[topBlock-1].sections[cpsStmts])
setLen(p.blocks, topBlock)
# this is done after the block is popped so $n is
# properly indented when pretty printing is enabled
@@ -171,7 +171,7 @@ proc endBlock(p: BProc) =
var blockEnd: Rope
if frameLen > 0:
blockEnd.addf("FR_.len-=$1;$n", [frameLen.rope])
if p.blocks[topBlock].label != nil:
if p.blocks[topBlock].label.len != 0:
blockEnd.addf("} $1: ;$n", [p.blocks[topBlock].label])
else:
blockEnd.addf("}$n", [])
@@ -279,17 +279,15 @@ proc genGotoVar(p: BProc; value: PNode) =
else:
lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType): Rope
proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Rope)
proc potentialValueInit(p: BProc; v: PSym; value: PNode): Rope =
proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) =
if lfDynamicLib in v.loc.flags or sfThread in v.flags or p.hcrOn:
result = nil
discard "nothing to do"
elif sfGlobal in v.flags and value != nil and isDeepConstExpr(value, p.module.compileToCpp) and
p.withinLoop == 0 and not containsGarbageCollectedRef(v.typ):
#echo "New code produced for ", v.name.s, " ", p.config $ value.info
result = genBracedInit(p, value, isConst = false, v.typ)
else:
result = nil
genBracedInit(p, value, isConst = false, v.typ, result)
proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
if sfGoto in v.flags:
@@ -297,8 +295,8 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
genGotoVar(p, value)
return
var targetProc = p
var traverseProc: Rope
let valueAsRope = potentialValueInit(p, v, value)
var valueAsRope = ""
potentialValueInit(p, v, value, valueAsRope)
if sfGlobal in v.flags:
if v.flags * {sfImportc, sfExportc} == {sfImportc} and
value.kind == nkEmpty and
@@ -314,7 +312,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
# That's why we are doing the construction inside the preInitProc.
# genObjectInit relies on the C runtime's guarantees that
# global variables will be initialized to zero.
if valueAsRope == nil:
if valueAsRope.len == 0:
var loc = v.loc
# When the native TLS is unavailable, a global thread-local variable needs
@@ -328,9 +326,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
# if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc)
if sfExportc in v.flags and p.module.g.generatedHeader != nil:
genVarPrototype(p.module.g.generatedHeader, vn)
traverseProc = getTraverseProc(p, v)
if traverseProc != nil and not p.hcrOn:
registerTraverseProc(p, v, traverseProc)
registerTraverseProc(p, v)
else:
let imm = isAssignedImmediately(p.config, value)
if imm and p.module.compileToCpp and p.splitDecls == 0 and
@@ -343,14 +339,14 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
var tmp: TLoc
if value.kind in nkCallKinds and value[0].kind == nkSym and
sfConstructor in value[0].sym.flags:
var params: Rope
var params = newRopeAppender()
var argsCounter = 0
let typ = skipTypes(value[0].typ, abstractInst)
assert(typ.kind == tyProc)
for i in 1..<value.len:
if params != nil: params.add(~", ")
assert(typ.len == typ.n.len)
params.add(genOtherArg(p, value, i, typ))
if params == nil:
genOtherArg(p, value, i, typ, params, argsCounter)
if params.len == 0:
lineF(p, cpsStmts, "$#;$n", [decl])
else:
lineF(p, cpsStmts, "$#($#);$n", [decl, params])
@@ -361,7 +357,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
assignLocalVar(p, vn)
initLocalVar(p, v, imm)
if traverseProc == nil: traverseProc = ~"NULL"
let traverseProc = "NULL"
# If the var is in a block (control flow like if/while or a block) in global scope just
# register the so called "global" so it can be used later on. There is no need to close
# and reopen of if (nim_hcr_do_init_) blocks because we are in one already anyway.
@@ -381,7 +377,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
lineCg(targetProc, cpsStmts, "if (hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1))$N",
[v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
startBlock(targetProc)
if value.kind != nkEmpty and valueAsRope == nil:
if value.kind != nkEmpty and valueAsRope.len == 0:
genLineDir(targetProc, vn)
loadInto(targetProc, vn, value, v.loc)
if forHcr:
@@ -560,7 +556,9 @@ proc genComputedGoto(p: BProc; n: PNode) =
return
let val = getOrdValue(it[j])
lineF(p, cpsStmts, "TMP$#_:$n", [intLiteral(toInt64(val)+id+1)])
var lit = newRopeAppender()
intLiteral(toInt64(val)+id+1, lit)
lineF(p, cpsStmts, "TMP$#_:$n", [lit])
genStmts(p, it.lastSon)
@@ -614,8 +612,9 @@ proc genWhileStmt(p: BProc, t: PNode) =
p.blocks[p.breakIdx].isLoop = true
initLocExpr(p, t[0], a)
if (t[0].kind != nkIntLit) or (t[0].intVal == 0):
let label = assignLabel(p.blocks[p.breakIdx])
lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), label])
lineF(p, cpsStmts, "if (!$1) goto ", [rdLoc(a)])
assignLabel(p.blocks[p.breakIdx], p.s(cpsStmts))
lineF(p, cpsStmts, ";$n", [])
genStmts(p, loopBody)
if optProfiler in p.options:
@@ -702,12 +701,12 @@ proc genBreakStmt(p: BProc, t: PNode) =
while idx >= 0 and not p.blocks[idx].isLoop: dec idx
if idx < 0 or not p.blocks[idx].isLoop:
internalError(p.config, t.info, "no loop to break")
let label = assignLabel(p.blocks[idx])
p.blocks[idx].label = "LA" & p.blocks[idx].id.rope
blockLeaveActions(p,
p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts,
p.inExceptBlockLen - p.blocks[idx].nestedExceptStmts)
genLineDir(p, t)
lineF(p, cpsStmts, "goto $1;$n", [label])
lineF(p, cpsStmts, "goto $1;$n", [p.blocks[idx].label])
proc raiseExit(p: BProc) =
assert p.config.exc == excGoto
@@ -729,21 +728,19 @@ proc finallyActions(p: BProc) =
if finallyBlock != nil:
genSimpleBlock(p, finallyBlock[0])
proc raiseInstr(p: BProc): Rope =
proc raiseInstr(p: BProc; result: var Rope) =
if p.config.exc == excGoto:
let L = p.nestedTryStmts.len
if L == 0:
p.flags.incl beforeRetNeeded
# easy case, simply goto 'ret':
result = ropecg(p.module, "goto BeforeRet_;$n", [])
result.add ropecg(p.module, "goto BeforeRet_;$n", [])
else:
# raise inside an 'except' must go to the finally block,
# raise outside an 'except' block must go to the 'except' list.
result = ropecg(p.module, "goto LA$1_;$n",
result.add ropecg(p.module, "goto LA$1_;$n",
[p.nestedTryStmts[L-1].label])
# + ord(p.nestedTryStmts[L-1].inExcept)])
else:
result = nil
proc genRaiseStmt(p: BProc, t: PNode) =
if t[0].kind != nkEmpty:
@@ -772,12 +769,10 @@ proc genRaiseStmt(p: BProc, t: PNode) =
genLineDir(p, t)
# reraise the last exception:
if p.config.exc == excCpp:
line(p, cpsStmts, ~"throw;$n")
line(p, cpsStmts, "throw;\n")
else:
linefmt(p, cpsStmts, "#reraiseException();$n", [])
let gotoInstr = raiseInstr(p)
if gotoInstr != nil:
line(p, cpsStmts, gotoInstr)
raiseInstr(p, p.s(cpsStmts))
template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
rangeFormat, eqFormat: FormatStr, labl: TLabel) =
@@ -885,9 +880,11 @@ proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n",
[rdLoc(a), bitMask])
for j in 0..high(branches):
if branches[j] != nil:
if branches[j] != "":
var lit = newRopeAppender()
intLiteral(j, lit)
lineF(p, cpsStmts, "case $1: $n$2break;$n",
[intLiteral(j), branches[j]])
[lit, branches[j]])
lineF(p, cpsStmts, "}$n", []) # else statement:
if t[^1].kind != nkOfBranch:
lineF(p, cpsStmts, "goto LA$1_;$n", [rope(p.labels)])
@@ -921,16 +918,22 @@ proc genCaseRange(p: BProc, branch: PNode) =
for j in 0..<branch.len-1:
if branch[j].kind == nkRange:
if hasSwitchRange in CC[p.config.cCompiler].props:
lineF(p, cpsStmts, "case $1 ... $2:$n", [
genLiteral(p, branch[j][0]),
genLiteral(p, branch[j][1])])
var litA = newRopeAppender()
var litB = newRopeAppender()
genLiteral(p, branch[j][0], litA)
genLiteral(p, branch[j][1], litB)
lineF(p, cpsStmts, "case $1 ... $2:$n", [litA, litB])
else:
var v = copyNode(branch[j][0])
while v.intVal <= branch[j][1].intVal:
lineF(p, cpsStmts, "case $1:$n", [genLiteral(p, v)])
var litA = newRopeAppender()
genLiteral(p, v, litA)
lineF(p, cpsStmts, "case $1:$n", [litA])
inc(v.intVal)
else:
lineF(p, cpsStmts, "case $1:$n", [genLiteral(p, branch[j])])
var litA = newRopeAppender()
genLiteral(p, branch[j], litA)
lineF(p, cpsStmts, "case $1:$n", [litA])
proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
# analyse 'case' statement:
@@ -942,7 +945,7 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
var lend = if splitPoint > 0: genIfForCaseUntil(p, n, d,
rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n",
eqFormat = "if ($1 == $2) goto $3;$n",
splitPoint, a) else: nil
splitPoint, a) else: ""
# generate switch part (might be empty):
if splitPoint+1 < n.len:
@@ -963,7 +966,7 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
if (hasAssume in CC[p.config.cCompiler].props) and not hasDefault:
lineF(p, cpsStmts, "default: __assume(0);$n", [])
lineF(p, cpsStmts, "}$n", [])
if lend != nil: fixLabel(p, lend)
if lend != "": fixLabel(p, lend)
proc genCase(p: BProc, t: PNode, d: var TLoc) =
genLineDir(p, t)
@@ -1064,7 +1067,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
endBlock(p)
else:
var orExpr = Rope(nil)
var orExpr = newRopeAppender()
var exvar = PNode(nil)
for j in 0..<t[i].len - 1:
var typeNode = t[i][j]
@@ -1075,7 +1078,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
if isImportedException(typeNode.typ, p.config):
hasImportedCppExceptions = true
else:
if orExpr != nil: orExpr.add("||")
if orExpr.len != 0: orExpr.add("||")
let checkFor = if optTinyRtti in p.config.globalOptions:
genTypeInfo2Name(p.module, typeNode.typ)
else:
@@ -1083,14 +1086,15 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
if orExpr != nil:
if orExpr.len != 0:
if hasIf:
startBlock(p, "else if ($1) {$n", [orExpr])
else:
startBlock(p, "if ($1) {$n", [orExpr])
hasIf = true
if exvar != nil:
fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), OnStack)
fillLocalName(p, exvar.sym)
fillLoc(exvar.sym.loc, locTemp, exvar, OnStack)
linefmt(p, cpsStmts, "$1 $2 = T$3_;$n", [getTypeDesc(p.module, exvar.sym.typ),
rdLoc(exvar.sym.loc), rope(etmp+1)])
# we handled the error:
@@ -1131,7 +1135,8 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
typeNode = t[i][j][1]
if isImportedException(typeNode.typ, p.config):
let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), OnStack)
fillLocalName(p, exvar.sym)
fillLoc(exvar.sym.loc, locTemp, exvar, OnStack)
startBlock(p, "catch ($1& $2) {$n", getTypeDesc(p.module, typeNode.typ), rdLoc(exvar.sym.loc))
genExceptBranchBody(t[i][^1]) # exception handler body will duplicated for every type
endBlock(p)
@@ -1184,7 +1189,7 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
if not isEmptyType(t.typ) and d.k == locNone:
getTemp(p, t.typ, d)
genLineDir(p, t)
discard cgsym(p.module, "popCurrentExceptionEx")
cgsym(p.module, "popCurrentExceptionEx")
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
p.nestedTryStmts.add((fin, false, 0.Natural))
startBlock(p, "try {$n")
@@ -1210,7 +1215,8 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
for j in 0..<t[i].len-1:
if t[i][j].isInfixAs():
let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), OnUnknown)
fillLocalName(p, exvar.sym)
fillLoc(exvar.sym.loc, locTemp, exvar, OnUnknown)
startBlock(p, "catch ($1& $2) {$n", getTypeDesc(p.module, t[i][j][1].typ), rdLoc(exvar.sym.loc))
else:
startBlock(p, "catch ($1&) {$n", getTypeDesc(p.module, t[i][j].typ))
@@ -1225,7 +1231,7 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
# finally requires catch all presence
startBlock(p, "catch (...) {$n")
genStmts(p, t[^1][0])
line(p, cpsStmts, ~"throw;$n")
line(p, cpsStmts, "throw;\n")
endBlock(p)
genSimpleBlock(p, t[^1][0])
@@ -1287,10 +1293,10 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
linefmt(p, cpsStmts, "*nimErr_ = NIM_FALSE;$n", [])
expr(p, t[i][0], d)
else:
var orExpr: Rope = nil
var orExpr = newRopeAppender()
for j in 0..<t[i].len - 1:
assert(t[i][j].kind == nkType)
if orExpr != nil: orExpr.add("||")
if orExpr.len != 0: orExpr.add("||")
let checkFor = if optTinyRtti in p.config.globalOptions:
genTypeInfo2Name(p.module, t[i][j].typ)
else:
@@ -1372,7 +1378,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
else:
p.flags.incl noSafePoints
genLineDir(p, t)
discard cgsym(p.module, "Exception")
cgsym(p.module, "Exception")
var safePoint: Rope
if not quirkyExceptions:
safePoint = getTempName(p.module)
@@ -1389,7 +1395,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
if isDefined(p.config, "vcc") or isDefined(p.config, "clangcl"):
# For the vcc compiler, use `setjmp()` with one argument.
# See https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setjmp?view=msvc-170
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
else:
# The Windows `_setjmp()` takes two arguments, with the second being an
# undocumented buffer used by the SEH mechanism for stack unwinding.
@@ -1431,10 +1437,10 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
endBlock(p)
else:
var orExpr: Rope = nil
var orExpr = newRopeAppender()
for j in 0..<t[i].len - 1:
assert(t[i][j].kind == nkType)
if orExpr != nil: orExpr.add("||")
if orExpr.len != 0: orExpr.add("||")
let checkFor = if optTinyRtti in p.config.globalOptions:
genTypeInfo2Name(p.module, t[i][j].typ)
else:
@@ -1466,7 +1472,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
if not quirkyExceptions:
linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", [safePoint])
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) =
var res = ""
for it in t.sons:
case it.kind
@@ -1482,13 +1488,8 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
res.add($getTypeDesc(p.module, sym.typ))
else:
discard getTypeDesc(p.module, skipTypes(sym.typ, abstractPtrs))
var r = sym.loc.r
if r == nil:
# if no name has already been given,
# it doesn't matter much:
r = mangleName(p.module, sym)
sym.loc.r = r # but be consequent!
res.add($r)
fillBackendName(p.module, sym)
res.add($sym.loc.r)
of nkTypeOfExpr:
res.add($getTypeDesc(p.module, it.typ))
else:
@@ -1513,12 +1514,13 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
result.add("\\n\"\n")
else:
res.add("\L")
result = res.rope
result.add res.rope
proc genAsmStmt(p: BProc, t: PNode) =
assert(t.kind == nkAsmStmt)
genLineDir(p, t)
var s = genAsmOrEmitStmt(p, t, isAsmStmt=true)
var s = newRopeAppender()
genAsmOrEmitStmt(p, t, isAsmStmt=true, s)
# see bug #2362, "top level asm statements" seem to be a mis-feature
# but even if we don't do this, the example in #2362 cannot possibly
# work:
@@ -1526,7 +1528,8 @@ proc genAsmStmt(p: BProc, t: PNode) =
# top level asm statement?
p.module.s[cfsProcHeaders].add runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s])
else:
p.s(cpsStmts).add indentLine(p, runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s]))
addIndent p, p.s(cpsStmts)
p.s(cpsStmts).add runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s])
proc determineSection(n: PNode): TCFileSection =
result = cfsProcHeaders
@@ -1537,7 +1540,8 @@ proc determineSection(n: PNode): TCFileSection =
elif sec.startsWith("/*INCLUDESECTION*/"): result = cfsHeaders
proc genEmit(p: BProc, t: PNode) =
var s = genAsmOrEmitStmt(p, t[1])
var s = newRopeAppender()
genAsmOrEmitStmt(p, t[1], false, s)
if p.prc == nil:
# top level emit pragma?
let section = determineSection(t[1])
@@ -1562,10 +1566,12 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType,
if not containsOrIncl(p.module.declaredThings, field.id):
appcg(p.module, cfsVars, "extern $1",
[discriminatorTableDecl(p.module, t, field)])
var lit = newRopeAppender()
intLiteral(toInt64(lengthOrd(p.config, field.typ))+1, lit)
lineCg(p, cpsStmts,
"#FieldDiscriminantCheck((NI)(NU)($1), (NI)(NU)($2), $3, $4);$n",
[rdLoc(a), rdLoc(tmp), discriminatorTableName(p.module, t, field),
intLiteral(toInt64(lengthOrd(p.config, field.typ))+1)])
lit])
when false:
proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLoc) =

View File

@@ -44,13 +44,13 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
m.s[cfsVars].addf(" $1;$n", [s.loc.r])
proc generateThreadLocalStorage(m: BModule) =
if m.g.nimtv != nil and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
if m.g.nimtv != "" and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
for t in items(m.g.nimtvDeps): discard getTypeDesc(m, t)
finishTypeDescriptions(m)
m.s[cfsSeqTypes].addf("typedef struct {$1} NimThreadVars;$n", [m.g.nimtv])
proc generateThreadVarsSize(m: BModule) =
if m.g.nimtv != nil:
if m.g.nimtv != "":
let externc = if m.config.backend == backendCpp or
sfCompileToCpp in m.module.flags: "extern \"C\" "
else: ""

View File

@@ -34,7 +34,7 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
if (n[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
var p = c.p
let disc = n[0].sym
if disc.loc.r == nil: fillObjectFields(c.p.module, typ)
if disc.loc.r == "": fillObjectFields(c.p.module, typ)
if disc.loc.t == nil:
internalError(c.p.config, n.info, "genTraverseProc()")
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
@@ -51,7 +51,7 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
of nkSym:
let field = n.sym
if field.typ.kind == tyVoid: return
if field.loc.r == nil: fillObjectFields(c.p.module, typ)
if field.loc.r == "": fillObjectFields(c.p.module, typ)
if field.loc.t == nil:
internalError(c.p.config, n.info, "genTraverseProc()")
genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
@@ -76,7 +76,8 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
let arraySize = lengthOrd(c.p.config, typ[0])
var i: TLoc
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i)
let oldCode = p.s(cpsStmts)
var oldCode = p.s(cpsStmts)
freeze oldCode
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
[i.r, arraySize])
let oldLen = p.s(cpsStmts).len
@@ -120,7 +121,8 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
assert typ.kind == tySequence
var i: TLoc
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i)
let oldCode = p.s(cpsStmts)
var oldCode = p.s(cpsStmts)
freeze oldCode
var a: TLoc
a.r = accessor

View File

@@ -13,8 +13,6 @@
import sighashes, modulegraphs
proc genProcHeader(m: BModule, prc: PSym, asPtr: bool = false): Rope
proc isKeyword(w: PIdent): bool =
# Nim and C++ share some keywords
# it's more efficient to test the whole Nim keywords range
@@ -35,10 +33,9 @@ proc mangleField(m: BModule; name: PIdent): string =
if isKeyword(name):
result.add "_0"
proc mangleName(m: BModule; s: PSym): Rope =
result = s.loc.r
if result == nil:
result = s.name.s.mangle.rope
proc fillBackendName(m: BModule; s: PSym) =
if s.loc.r == "":
var result = s.name.s.mangle.rope
result.add "__"
result.add m.g.graph.ifaces[s.itemId.module].uniqueName
result.add "_"
@@ -49,12 +46,11 @@ proc mangleName(m: BModule; s: PSym): Rope =
s.loc.r = result
writeMangledName(m.ndi, s, m.config)
proc mangleParamName(m: BModule; s: PSym): Rope =
proc fillParamName(m: BModule; s: PSym) =
## we cannot use 'sigConflicts' here since we have a BModule, not a BProc.
## Fortunately C's scoping rules are sane enough so that that doesn't
## cause any trouble.
result = s.loc.r
if result == nil:
if s.loc.r == "":
var res = s.name.s.mangle
# Take into account if HCR is on because of the following scenario:
# if a module gets imported and it has some more importc symbols in it,
@@ -74,20 +70,16 @@ proc mangleParamName(m: BModule; s: PSym): Rope =
# executable file for the main module, which is running (or both!) -> error.
if m.hcrOn or isKeyword(s.name) or m.g.config.cppDefines.contains(res):
res.add "_0"
result = res.rope
s.loc.r = result
s.loc.r = res.rope
writeMangledName(m.ndi, s, m.config)
proc mangleLocalName(p: BProc; s: PSym): Rope =
proc fillLocalName(p: BProc; s: PSym) =
assert s.kind in skLocalVars+{skTemp}
#assert sfGlobal notin s.flags
result = s.loc.r
if result == nil:
if s.loc.r == "":
var key = s.name.s.mangle
when not defined(nimSeqsV2):
shallow(key)
let counter = p.sigConflicts.getOrDefault(key)
result = key.rope
var result = key.rope
if s.kind == skTemp:
# speed up conflict search for temps (these are quite common):
if counter != 0: result.add "_" & rope(counter+1)
@@ -103,8 +95,6 @@ proc scopeMangledParam(p: BProc; param: PSym) =
## generate unique identifiers reliably (consider that ``var a = a`` is
## even an idiom in Nim).
var key = param.name.s.mangle
when not defined(nimSeqsV2):
shallow(key)
p.sigConflicts.inc(key)
const
@@ -112,13 +102,12 @@ const
tyDistinct, tyRange, tyStatic, tyAlias, tySink,
tyInferred, tyOwned}
proc typeName(typ: PType): Rope =
proc typeName(typ: PType; result: var Rope) =
let typ = typ.skipTypes(irrelevantForBackend)
result =
if typ.sym != nil and typ.kind in {tyObject, tyEnum}:
rope($typ.kind & '_' & typ.sym.name.s.mangle)
else:
rope($typ.kind)
result.add $typ.kind
if typ.sym != nil and typ.kind in {tyObject, tyEnum}:
result.add "_"
result.add typ.sym.name.s.mangle
proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
var t = typ
@@ -131,14 +120,17 @@ proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
else:
break
let typ = if typ.kind in {tyAlias, tySink, tyOwned}: typ.lastSon else: typ
if typ.loc.r == nil:
typ.loc.r = typ.typeName & $sig
if typ.loc.r == "":
typ.typeName(typ.loc.r)
typ.loc.r.add $sig
else:
when defined(debugSigHashes):
# check consistency:
assert($typ.loc.r == $(typ.typeName & $sig))
var tn = newRopeAppender()
typ.typeName(tn)
assert($typ.loc.r == $(tn & $sig))
result = typ.loc.r
if result == nil: internalError(m.config, "getTypeName: " & $typ.kind)
if result == "": internalError(m.config, "getTypeName: " & $typ.kind)
proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
case int(getSize(conf, typ))
@@ -266,7 +258,7 @@ proc addAbiCheck(m: BModule, t: PType, name: Rope) =
proc fillResult(conf: ConfigRef; param: PNode, proctype: PType) =
fillLoc(param.sym.loc, locParam, param, ~"Result",
fillLoc(param.sym.loc, locParam, param, "Result",
OnStack)
let t = param.sym.typ
if mapReturnType(conf, t) != ctArray and isInvalidReturnType(conf, proctype):
@@ -292,11 +284,11 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
of tyString:
case detectStrVersion(m)
of 2:
discard cgsym(m, "NimStrPayload")
discard cgsym(m, "NimStringV2")
cgsym(m, "NimStrPayload")
cgsym(m, "NimStringV2")
result = typeNameOrLiteral(m, typ, "NimStringV2")
else:
discard cgsym(m, "NimStringDesc")
cgsym(m, "NimStringDesc")
result = typeNameOrLiteral(m, typ, "NimStringDesc*")
of tyCstring: result = typeNameOrLiteral(m, typ, "NCSTRING")
of tyBool: result = typeNameOrLiteral(m, typ, "NIM_BOOL")
@@ -310,11 +302,11 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
else: internalError(m.config, "tyStatic for getSimpleTypeDesc")
of tyGenericInst, tyAlias, tySink, tyOwned:
result = getSimpleTypeDesc(m, lastSon typ)
else: result = nil
else: result = ""
if result != nil and typ.isImportedType():
if result != "" and typ.isImportedType():
let sig = hashType typ
if cacheGetType(m.typeCache, sig) == nil:
if cacheGetType(m.typeCache, sig) == "":
m.typeCache[sig] = result
proc pushType(m: BModule, typ: PType) =
@@ -327,7 +319,7 @@ proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope =
if typ == nil: result = rope("void")
else:
result = getSimpleTypeDesc(m, typ)
if result == nil: result = cacheGetType(m.typeCache, sig)
if result == "": result = cacheGetType(m.typeCache, sig)
proc structOrUnion(t: PType): Rope =
let cachedUnion = rope("union")
@@ -348,9 +340,9 @@ proc seqStar(m: BModule): string =
proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
result = cacheGetType(m.forwTypeCache, sig)
if result != nil: return
if result != "": return
result = getTypePre(m, typ, sig)
if result != nil: return
if result != "": return
let concrete = typ.skipTypes(abstractInst)
case concrete.kind
of tySequence, tyTuple, tyObject:
@@ -382,7 +374,7 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TSymKind): R
internalError(m.config, "cannot map the empty seq type to a C type")
result = cacheGetType(m.forwTypeCache, sig)
if result == nil:
if result == "":
result = getTypeName(m, t, sig)
if not isImportedType(t):
m.forwTypeCache[sig] = result
@@ -390,7 +382,7 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TSymKind): R
let payload = result & "_Content"
addForwardStructFormat(m, rope"struct", payload)
if cacheGetType(m.typeCache, sig) == nil:
if cacheGetType(m.typeCache, sig) == "":
m.typeCache[sig] = result
#echo "adding ", sig, " ", typeToString(t), " ", m.module.name.s
appcg(m, m.s[cfsTypes],
@@ -411,7 +403,7 @@ proc getSeqPayloadType(m: BModule; t: PType): Rope =
proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) =
let sig = hashType(t)
let result = cacheGetType(m.typeCache, sig)
if result == nil:
if result == "":
discard getTypeDescAux(m, t, check, skVar)
else:
# little hack for now to prevent multiple definitions of the same
@@ -433,30 +425,31 @@ proc paramStorageLoc(param: PSym): TStorageLoc =
proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
check: var IntSet, declareEnvironment=true;
weakDep=false) =
params = nil
params = ""
if t[0] == nil or isInvalidReturnType(m.config, t):
rettype = ~"void"
rettype = "void"
else:
rettype = getTypeDescAux(m, t[0], check, skResult)
for i in 1..<t.n.len:
if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genProcParams")
var param = t.n[i].sym
if isCompileTimeOnly(param.typ): continue
if params != nil: params.add(~", ")
fillLoc(param.loc, locParam, t.n[i], mangleParamName(m, param),
if params != "": params.add(", ")
fillParamName(m, param)
fillLoc(param.loc, locParam, t.n[i],
param.paramStorageLoc)
if ccgIntroducedPtr(m.config, param, t[0]):
params.add(getTypeDescWeak(m, param.typ, check, skParam))
params.add(~"*")
params.add("*")
incl(param.loc.flags, lfIndirect)
param.loc.storage = OnUnknown
elif weakDep:
params.add(getTypeDescWeak(m, param.typ, check, skParam))
else:
params.add(getTypeDescAux(m, param.typ, check, skParam))
params.add(~" ")
params.add(" ")
if sfNoalias in param.flags:
params.add(~"NIM_NOALIAS ")
params.add("NIM_NOALIAS ")
params.add(param.loc.r)
# declare the len field for open arrays:
var arr = param.typ.skipTypes({tyGenericInst})
@@ -471,7 +464,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
arr = arr[0].skipTypes({tySink})
if t[0] != nil and isInvalidReturnType(m.config, t):
var arr = t[0]
if params != nil: params.add(", ")
if params != "": params.add(", ")
if mapReturnType(m.config, t[0]) != ctArray:
if isHeaderFile in m.flags:
# still generates types for `--header`
@@ -484,12 +477,12 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
params.add(getTypeDescAux(m, arr, check, skResult))
params.addf(" Result", [])
if t.callConv == ccClosure and declareEnvironment:
if params != nil: params.add(", ")
if params != "": params.add(", ")
params.add("void* ClE_0")
if tfVarargs in t.flags:
if params != nil: params.add(", ")
if params != "": params.add(", ")
params.add("...")
if params == nil: params.add("void)")
if params == "": params.add("void)")
else: params.add(")")
params = "(" & params
@@ -498,30 +491,30 @@ proc mangleRecFieldName(m: BModule; field: PSym): Rope =
result = field.loc.r
else:
result = rope(mangleField(m, field.name))
if result == nil: internalError(m.config, field.info, "mangleRecFieldName")
if result == "": internalError(m.config, field.info, "mangleRecFieldName")
proc genRecordFieldsAux(m: BModule, n: PNode,
rectype: PType,
check: var IntSet, unionPrefix = ""): Rope =
result = nil
check: var IntSet; result: var Rope; unionPrefix = "") =
case n.kind
of nkRecList:
for i in 0..<n.len:
result.add(genRecordFieldsAux(m, n[i], rectype, check, unionPrefix))
genRecordFieldsAux(m, n[i], rectype, check, result, unionPrefix)
of nkRecCase:
if n[0].kind != nkSym: internalError(m.config, n.info, "genRecordFieldsAux")
result.add(genRecordFieldsAux(m, n[0], rectype, check, unionPrefix))
genRecordFieldsAux(m, n[0], rectype, check, result, unionPrefix)
# prefix mangled name with "_U" to avoid clashes with other field names,
# since identifiers are not allowed to start with '_'
var unionBody: Rope = nil
var unionBody: Rope = ""
for i in 1..<n.len:
case n[i].kind
of nkOfBranch, nkElse:
let k = lastSon(n[i])
if k.kind != nkSym:
let structName = "_" & mangleRecFieldName(m, n[0].sym) & "_" & $i
let a = genRecordFieldsAux(m, k, rectype, check, unionPrefix & $structName & ".")
if a != nil:
var a = newRopeAppender()
genRecordFieldsAux(m, k, rectype, check, a, unionPrefix & $structName & ".")
if a != "":
if tfPacked notin rectype.flags:
unionBody.add("struct {")
else:
@@ -534,9 +527,9 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props:
unionBody.addf("#pragma pack(pop)$n", [])
else:
unionBody.add(genRecordFieldsAux(m, k, rectype, check, unionPrefix))
genRecordFieldsAux(m, k, rectype, check, unionBody, unionPrefix)
else: internalError(m.config, "genRecordFieldsAux(record case branch)")
if unionBody != nil:
if unionBody != "":
result.addf("union{$n$1};$n", [unionBody])
of nkSym:
let field = n.sym
@@ -550,7 +543,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
# have to recurse via 'getTypeDescAux'. And not doing so prevents problems
# with heavily templatized C++ code:
if not isImportedCppType(rectype):
let noAlias = if sfNoalias in field.flags: ~" NIM_NOALIAS" else: nil
let noAlias = if sfNoalias in field.flags: " NIM_NOALIAS" else: ""
let fieldType = field.loc.lode.typ.skipTypes(abstractInst)
if fieldType.kind == tyUncheckedArray:
@@ -568,7 +561,8 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
else: internalError(m.config, n.info, "genRecordFieldsAux()")
proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope =
result = genRecordFieldsAux(m, typ.n, typ, check)
result = newRopeAppender()
genRecordFieldsAux(m, typ.n, typ, check, result)
proc fillObjectFields*(m: BModule; typ: PType) =
# sometimes generic objects are not consistently merged. We patch over
@@ -625,7 +619,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
result.addf(" {$n", [name])
let desc = getRecordFields(m, typ, check)
if desc == nil and not hasField:
if desc == "" and not hasField:
result.addf("char dummy;$n", [])
else:
result.add(desc)
@@ -636,11 +630,11 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
proc getTupleDesc(m: BModule, typ: PType, name: Rope,
check: var IntSet): Rope =
result = "$1 $2 {$n" % [structOrUnion(typ), name]
var desc: Rope = nil
var desc: Rope = ""
for i in 0..<typ.len:
desc.addf("$1 Field$2;$n",
[getTypeDescAux(m, typ[i], check, skField), rope(i)])
if desc == nil: result.add("char dummy;\L")
if desc == "": result.add("char dummy;\L")
else: result.add(desc)
result.add("};\L")
@@ -679,7 +673,7 @@ proc getOpenArrayDesc(m: BModule, t: PType, check: var IntSet; kind: TSymKind):
result = getTypeDescWeak(m, t[0], check, kind) & "*"
else:
result = cacheGetType(m.typeCache, sig)
if result == nil:
if result == "":
result = getTypeName(m, t, sig)
m.typeCache[sig] = result
let elemType = getTypeDescWeak(m, t[0], check, kind)
@@ -705,7 +699,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
addAbiCheck(m, t, result)
result = getTypePre(m, t, sig)
if result != nil and t.kind != tyOpenArray:
if result != "" and t.kind != tyOpenArray:
excl(check, t.id)
return
case t.kind
@@ -748,7 +742,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
result = getOpenArrayDesc(m, t, check, kind)
of tyEnum:
result = cacheGetType(m.typeCache, sig)
if result == nil:
if result == "":
result = getTypeName(m, origTyp, sig)
if not (isImportedCppType(t) or
(sfImportc in t.sym.flags and t.sym.magic == mNone)):
@@ -796,12 +790,12 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
# we cannot use getTypeForward here because then t would be associated
# with the name of the struct, not with the pointer to the struct:
result = cacheGetType(m.forwTypeCache, sig)
if result == nil:
if result == "":
result = getTypeName(m, origTyp, sig)
if not isImportedType(t):
addForwardStructFormat(m, structOrUnion(t), result)
m.forwTypeCache[sig] = result
assert(cacheGetType(m.typeCache, sig) == nil)
assert(cacheGetType(m.typeCache, sig) == "")
m.typeCache[sig] = result & seqStar(m)
if not isImportedType(t):
if skipTypes(t[0], typedescInst).kind != tyEmpty:
@@ -837,25 +831,26 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
[foo, result, rope(n)])
of tyObject, tyTuple:
if isImportedCppType(t) and origTyp.kind == tyGenericInst:
let cppName = getTypeName(m, t, sig)
let cppNameAsRope = getTypeName(m, t, sig)
let cppName = $cppNameAsRope
var i = 0
var chunkStart = 0
template addResultType(ty: untyped) =
if ty == nil or ty.kind == tyVoid:
result.add(~"void")
result.add("void")
elif ty.kind == tyStatic:
internalAssert m.config, ty.n != nil
result.add ty.n.renderTree
else:
result.add getTypeDescAux(m, ty, check, kind)
while i < cppName.data.len:
if cppName.data[i] == '\'':
while i < cppName.len:
if cppName[i] == '\'':
var chunkEnd = i-1
var idx, stars: int
if scanCppGenericSlot(cppName.data, i, idx, stars):
result.add cppName.data.substr(chunkStart, chunkEnd)
if scanCppGenericSlot(cppName, i, idx, stars):
result.add cppName.substr(chunkStart, chunkEnd)
chunkStart = i
let typeInSlot = resolveStarsInCppType(origTyp, idx + 1, stars)
@@ -864,9 +859,9 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
inc i
if chunkStart != 0:
result.add cppName.data.substr(chunkStart)
result.add cppName.substr(chunkStart)
else:
result = cppName & "<"
result = cppNameAsRope & "<"
for i in 1..<origTyp.len-1:
if i > 1: result.add(" COMMA ")
addResultType(origTyp[i])
@@ -877,13 +872,13 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
# The resulting type will include commas and these won't play well
# with the C macros for defining procs such as N_NIMCALL. We must
# create a typedef for the type and use it in the proc signature:
let typedefName = ~"TY" & $sig
let typedefName = "TY" & $sig
m.s[cfsTypes].addf("typedef $1 $2;$n", [result, typedefName])
m.typeCache[sig] = typedefName
result = typedefName
else:
result = cacheGetType(m.forwTypeCache, sig)
if result == nil:
if result == "":
result = getTypeName(m, origTyp, sig)
m.forwTypeCache[sig] = result
if not isImportedType(t):
@@ -899,7 +894,9 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
discard # addAbiCheck(m, t, result) # already handled elsewhere
of tySet:
# Don't use the imported name as it may be scoped: 'Foo::SomeKind'
result = $t.kind & '_' & t.lastSon.typeName & $t.lastSon.hashType
result = rope("tySet_")
t.lastSon.typeName(result)
result.add $t.lastSon.hashType
m.typeCache[sig] = result
if not isImportedType(t):
let s = int(getSize(m.config, t))
@@ -912,7 +909,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
result = getTypeDescAux(m, lastSon(t), check, kind)
else:
internalError(m.config, "getTypeDescAux(" & $t.kind & ')')
result = nil
result = ""
# fixes bug #145:
excl(check, t.id)
@@ -963,21 +960,12 @@ proc isReloadable(m: BModule, prc: PSym): bool =
proc isNonReloadable(m: BModule, prc: PSym): bool =
return m.hcrOn and sfNonReloadable in prc.flags
proc genProcHeader(m: BModule, prc: PSym, asPtr: bool = false): Rope =
var
rettype, params: Rope
proc genProcHeader(m: BModule, prc: PSym; result: var Rope; asPtr: bool = false) =
# using static is needed for inline procs
if lfExportLib in prc.loc.flags:
if isHeaderFile in m.flags:
result.add "N_LIB_IMPORT "
else:
result.add "N_LIB_EXPORT "
elif prc.typ.callConv == ccInline or asPtr or isNonReloadable(m, prc):
result.add "static "
elif sfImportc notin prc.flags:
result.add "N_LIB_PRIVATE "
var check = initIntSet()
fillLoc(prc.loc, locProc, prc.ast[namePos], mangleName(m, prc), OnUnknown)
fillBackendName(m, prc)
fillLoc(prc.loc, locProc, prc.ast[namePos], OnUnknown)
var rettype, params: Rope
genProcParams(m, prc.typ, rettype, params, check)
# handle the 2 options for hotcodereloading codegen - function pointer
# (instead of forward declaration) or header for function body with "_actual" postfix
@@ -988,12 +976,21 @@ proc genProcHeader(m: BModule, prc: PSym, asPtr: bool = false): Rope =
# careful here! don't access ``prc.ast`` as that could reload large parts of
# the object graph!
if prc.constraint.isNil:
if lfExportLib in prc.loc.flags:
if isHeaderFile in m.flags:
result.add "N_LIB_IMPORT "
else:
result.add "N_LIB_EXPORT "
elif prc.typ.callConv == ccInline or asPtr or isNonReloadable(m, prc):
result.add "static "
elif sfImportc notin prc.flags:
result.add "N_LIB_PRIVATE "
result.addf("$1$2($3, $4)$5",
[rope(CallingConvToStr[prc.typ.callConv]), asPtrStr, rettype, name,
params])
else:
let asPtrStr = if asPtr: (rope("(*") & name & ")") else: name
result = runtimeFormat(prc.cgDeclFrmt, [rettype, asPtrStr, params])
result.add runtimeFormat(prc.cgDeclFrmt, [rettype, asPtrStr, params])
# ------------------ type info generation -------------------------------------
@@ -1032,7 +1029,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
#else echo("can contain a cycle: " & typeToString(typ))
if flags != 0:
m.s[cfsTypeInit3].addf("$1.flags = $2;$n", [nameHcr, rope(flags)])
discard cgsym(m, "TNimType")
cgsym(m, "TNimType")
if isDefined(m.config, "nimTypeNames"):
var typename = typeToString(if origType.typeInst != nil: origType.typeInst
else: origType, preferName)
@@ -1040,16 +1037,16 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
typename = "anon ref object from " & m.config$origType.skipTypes(skipPtrs).sym.info
m.s[cfsTypeInit3].addf("$1.name = $2;$n",
[nameHcr, makeCString typename])
discard cgsym(m, "nimTypeRoot")
cgsym(m, "nimTypeRoot")
m.s[cfsTypeInit3].addf("$1.nextType = nimTypeRoot; nimTypeRoot=&$1;$n",
[nameHcr])
if m.hcrOn:
m.s[cfsData].addf("static TNimType* $1;$n", [name])
m.s[cfsStrData].addf("static TNimType* $1;$n", [name])
m.hcrCreateTypeInfosProc.addf("\thcrRegisterGlobal($2, \"$1\", sizeof(TNimType), NULL, (void**)&$1);$n",
[name, getModuleDllPath(m, m.module)])
else:
m.s[cfsData].addf("N_LIB_PRIVATE TNimType $1;$n", [name])
m.s[cfsStrData].addf("N_LIB_PRIVATE TNimType $1;$n", [name])
proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope;
info: TLineInfo) =
@@ -1077,7 +1074,7 @@ proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope =
proc rope(arg: Int128): Rope = rope($arg)
proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): Rope =
discard cgsym(m, "TNimNode")
cgsym(m, "TNimNode")
var tmp = discriminatorTableName(m, objtype, d)
result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(m.config, d.typ)+1)]
@@ -1112,7 +1109,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
var tmp = discriminatorTableName(m, typ, field)
var L = lengthOrd(m.config, field.typ)
assert L > 0
if field.loc.r == nil: fillObjectFields(m, typ)
if field.loc.r == "": fillObjectFields(m, typ)
if field.loc.t == nil:
internalError(m.config, n.info, "genObjectFields")
m.s[cfsTypeInit3].addf("$1.kind = 3;$n" &
@@ -1150,7 +1147,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
# Do not produce code for void types
if isEmptyType(field.typ): return
if field.bitsize == 0:
if field.loc.r == nil: fillObjectFields(m, typ)
if field.loc.r == "": fillObjectFields(m, typ)
if field.loc.t == nil:
internalError(m.config, n.info, "genObjectFields")
m.s[cfsTypeInit3].addf("$1.kind = 1;$n" &
@@ -1244,7 +1241,7 @@ proc genSetInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
assert(typ[0] != nil)
genTypeInfoAux(m, typ, typ, name, info)
var tmp = getNimNode(m)
m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 0;$n" & "$3.node = &$1;$n",
m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 0;$n$3.node = &$1;$n",
[tmp, rope(firstOrd(m.config, typ)), tiNameForHcr(m, name)])
proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
@@ -1269,11 +1266,11 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
proc declareNimType(m: BModule, name: string; str: Rope, module: int) =
let nr = rope(name)
if m.hcrOn:
m.s[cfsData].addf("static $2* $1;$n", [str, nr])
m.s[cfsStrData].addf("static $2* $1;$n", [str, nr])
m.s[cfsTypeInit1].addf("\t$1 = ($3*)hcrGetGlobal($2, \"$1\");$n",
[str, getModuleDllPath(m, module), nr])
else:
m.s[cfsData].addf("extern $2 $1;$n", [str, nr])
m.s[cfsStrData].addf("extern $2 $1;$n", [str, nr])
proc genTypeInfo2Name(m: BModule; t: PType): Rope =
var res = "|"
@@ -1300,7 +1297,7 @@ proc genTypeInfo2Name(m: BModule; t: PType): Rope =
proc isTrivialProc(g: ModuleGraph; s: PSym): bool {.inline.} = getBody(g, s).len == 0
proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope =
proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result: var Rope) =
let theProc = getAttachedOp(m.g.graph, t, op)
if theProc != nil and not isTrivialProc(m.g.graph, theProc):
# the prototype of a destructor is ``=destroy(x: var T)`` and that of a
@@ -1311,7 +1308,7 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope =
theProc.name.s & " needs to have the 'nimcall' calling convention")
genProc(m, theProc)
result = theProc.loc.r
result.add theProc.loc.r
when false:
if not canFormAcycle(t) and op == attachedTrace:
@@ -1323,7 +1320,7 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope =
# unfortunately this check is wrong for an object type that only contains
# .cursor fields like 'Node' inside 'cycleleak'.
internalError(m.config, info, "no attached trace proc found")
result = rope("NIM_NIL")
result.add rope("NIM_NIL")
proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineInfo) =
var typeName: Rope
@@ -1335,17 +1332,23 @@ proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineIn
else:
typeName = rope("NIM_NIL")
discard cgsym(m, "TNimTypeV2")
m.s[cfsData].addf("N_LIB_PRIVATE TNimTypeV2 $1;$n", [name])
let destroyImpl = genHook(m, t, info, attachedDestructor)
let traceImpl = genHook(m, t, info, attachedTrace)
cgsym(m, "TNimTypeV2")
m.s[cfsStrData].addf("N_LIB_PRIVATE TNimTypeV2 $1;$n", [name])
var flags = 0
if not canFormAcycle(t): flags = flags or 1
addf(m.s[cfsTypeInit3], "$1.destructor = (void*)$2; $1.size = sizeof($3); $1.align = NIM_ALIGNOF($3); $1.name = $4;$n; $1.traceImpl = (void*)$5; $1.flags = $6;", [
name, destroyImpl, getTypeDesc(m, t), typeName,
traceImpl, rope(flags)])
var typeEntry = newRopeAppender()
addf(typeEntry, "$1.destructor = (void*)", [name])
genHook(m, t, info, attachedDestructor, typeEntry)
addf(typeEntry, "; $1.traceImpl = (void*)", [name])
genHook(m, t, info, attachedTrace, typeEntry)
addf(typeEntry, "; $1.name = $2;$n; $1.size = sizeof($3); $1.align = NIM_ALIGNOF($3); $1.flags = $4;",
[name, typeName, getTypeDesc(m, t), rope(flags)])
m.s[cfsTypeInit3].add typeEntry
if t.kind == tyObject and t.len > 0 and t[0] != nil and optEnableDeepCopy in m.config.globalOptions:
discard genTypeInfoV1(m, t, info)
@@ -1359,12 +1362,12 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope =
let sig = hashType(origType)
result = m.typeInfoMarkerV2.getOrDefault(sig)
if result != nil:
if result != "":
return prefixTI.rope & result & ")".rope
let marker = m.g.typeInfoMarkerV2.getOrDefault(sig)
if marker.str != nil:
discard cgsym(m, "TNimTypeV2")
if marker.str != "":
cgsym(m, "TNimTypeV2")
declareNimType(m, "TNimTypeV2", marker.str, marker.owner)
# also store in local type section:
m.typeInfoMarkerV2[sig] = marker.str
@@ -1378,7 +1381,7 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope =
# make sure the type info is created in the owner module
discard genTypeInfoV2(m.g.modules[owner], origType, info)
# reference the type info as extern here
discard cgsym(m, "TNimTypeV2")
cgsym(m, "TNimTypeV2")
declareNimType(m, "TNimTypeV2", result, owner)
return prefixTI.rope & result & ")".rope
@@ -1430,13 +1433,13 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope =
let sig = hashType(origType)
result = m.typeInfoMarker.getOrDefault(sig)
if result != nil:
if result != "":
return prefixTI.rope & result & ")".rope
let marker = m.g.typeInfoMarker.getOrDefault(sig)
if marker.str != nil:
discard cgsym(m, "TNimType")
discard cgsym(m, "TNimNode")
if marker.str != "":
cgsym(m, "TNimType")
cgsym(m, "TNimNode")
declareNimType(m, "TNimType", marker.str, marker.owner)
# also store in local type section:
m.typeInfoMarker[sig] = marker.str
@@ -1447,8 +1450,8 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope =
let old = m.g.graph.emittedTypeInfo.getOrDefault($result)
if old != FileIndex(0):
discard cgsym(m, "TNimType")
discard cgsym(m, "TNimNode")
cgsym(m, "TNimType")
cgsym(m, "TNimNode")
declareNimType(m, "TNimType", result, old.int)
return prefixTI.rope & result & ")".rope
@@ -1457,8 +1460,8 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope =
# make sure the type info is created in the owner module
discard genTypeInfoV1(m.g.modules[owner], origType, info)
# reference the type info as extern here
discard cgsym(m, "TNimType")
discard cgsym(m, "TNimNode")
cgsym(m, "TNimType")
cgsym(m, "TNimNode")
declareNimType(m, "TNimType", result, owner)
return prefixTI.rope & result & ")".rope
else:

View File

@@ -23,7 +23,7 @@ when defined(nimPreviewSlimSystem):
when not defined(leanCompiler):
import spawn, semparallel
import strutils except `%` # collides with ropes.`%`
import strutils except `%`, addf # collides with ropes.`%`
from ic / ic import ModuleBackendFlag
import dynlib
@@ -63,16 +63,23 @@ proc initLoc(result: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) =
result.k = k
result.storage = s
result.lode = lode
result.r = nil
result.r = ""
result.flags = {}
proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) =
proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) {.inline.} =
# fills the loc if it is not already initialized
if a.k == locNone:
a.k = k
a.lode = lode
a.storage = s
if a.r == "": a.r = r
proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) {.inline.} =
# fills the loc if it is not already initialized
if a.k == locNone:
a.k = k
a.lode = lode
a.storage = s
if a.r == nil: a.r = r
proc t(a: TLoc): PType {.inline.} =
if a.lode.kind == nkSym:
@@ -96,7 +103,8 @@ proc useHeader(m: BModule, sym: PSym) =
let str = getStr(sym.annex.path)
m.includeHeader(str)
proc cgsym(m: BModule, name: string): Rope
proc cgsym(m: BModule, name: string)
proc cgsymValue(m: BModule, name: string): Rope
proc getCFile(m: BModule): AbsoluteFile
@@ -113,10 +121,6 @@ proc getModuleDllPath(m: BModule, s: PSym): Rope =
import macros
proc cgFormatValue(result: var string; value: Rope) =
for str in leaves(value):
result.add str
proc cgFormatValue(result: var string; value: string) =
result.add value
@@ -197,7 +201,7 @@ macro ropecg(m: BModule, frmt: static[FormatStr], args: untyped): Rope =
var ident = newLit(substr(frmt, i, j-1))
i = j
flushStrLit()
result.add newCall(formatValue, resVar, newCall(ident"cgsym", m, ident))
result.add newCall(formatValue, resVar, newCall(ident"cgsymValue", m, ident))
elif frmt[i] == '#' and frmt[i+1] == '$':
inc(i, 2)
var j = 0
@@ -206,7 +210,7 @@ macro ropecg(m: BModule, frmt: static[FormatStr], args: untyped): Rope =
inc(i)
let ident = args[j-1]
flushStrLit()
result.add newCall(formatValue, resVar, newCall(ident"cgsym", m, ident))
result.add newCall(formatValue, resVar, newCall(ident"cgsymValue", m, ident))
var start = i
while i < frmt.len:
if frmt[i] != '$' and frmt[i] != '#': inc(i)
@@ -217,10 +221,9 @@ macro ropecg(m: BModule, frmt: static[FormatStr], args: untyped): Rope =
flushStrLit()
result.add newCall(ident"rope", resVar)
proc indentLine(p: BProc, r: Rope): Rope =
result = r
proc addIndent(p: BProc; result: var Rope) =
for i in 0..<p.blocks.len:
prepend(result, "\t".rope)
result.add "\t".rope
template appcg(m: BModule, c: var Rope, frmt: FormatStr,
args: untyped) =
@@ -234,23 +237,24 @@ template appcg(p: BProc, sec: TCProcSection, frmt: FormatStr,
args: untyped) =
p.s(sec).add(ropecg(p.module, frmt, args))
template line(p: BProc, sec: TCProcSection, r: Rope) =
p.s(sec).add(indentLine(p, r))
template line(p: BProc, sec: TCProcSection, r: string) =
p.s(sec).add(indentLine(p, r.rope))
addIndent p, p.s(sec)
p.s(sec).add(r)
template lineF(p: BProc, sec: TCProcSection, frmt: FormatStr,
args: untyped) =
p.s(sec).add(indentLine(p, frmt % args))
addIndent p, p.s(sec)
p.s(sec).add(frmt % args)
template lineCg(p: BProc, sec: TCProcSection, frmt: FormatStr,
args: untyped) =
p.s(sec).add(indentLine(p, ropecg(p.module, frmt, args)))
addIndent p, p.s(sec)
p.s(sec).add(ropecg(p.module, frmt, args))
template linefmt(p: BProc, sec: TCProcSection, frmt: FormatStr,
args: untyped) =
p.s(sec).add(indentLine(p, ropecg(p.module, frmt, args)))
addIndent p, p.s(sec)
p.s(sec).add(ropecg(p.module, frmt, args))
proc safeLineNm(info: TLineInfo): int =
result = toLinenumber(info)
@@ -276,7 +280,7 @@ proc genLineDir(p: BProc, t: PNode) =
let line = t.info.safeLineNm
if optEmbedOrigSrc in p.config.globalOptions:
p.s(cpsStmts).add(~"//" & sourceLine(p.config, t.info) & "\L")
p.s(cpsStmts).add("//" & sourceLine(p.config, t.info) & "\L")
genCLineDir(p.s(cpsStmts), toFullPath(p.config, t.info), line, p.config)
if ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and
(p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx:
@@ -287,7 +291,7 @@ proc genLineDir(p: BProc, t: PNode) =
proc accessThreadLocalVar(p: BProc, s: PSym)
proc emulatedThreadVars(conf: ConfigRef): bool {.inline.}
proc genProc(m: BModule, prc: PSym)
proc raiseInstr(p: BProc): Rope
proc raiseInstr(p: BProc; result: var Rope)
template compileToCpp(m: BModule): untyped =
m.config.backend == backendCpp or sfCompileToCpp in m.module.flags
@@ -298,10 +302,18 @@ proc getTempName(m: BModule): Rope =
proc rdLoc(a: TLoc): Rope =
# 'read' location (deref if indirect)
result = a.r
if lfIndirect in a.flags: result = "(*$1)" % [result]
if lfIndirect in a.flags:
result = "(*" & a.r & ")"
else:
result = a.r
proc lenField(p: BProc): Rope =
proc addRdLoc(a: TLoc; result: var Rope) =
if lfIndirect in a.flags:
result.add "(*" & a.r & ")"
else:
result.add a.r
proc lenField(p: BProc): Rope {.inline.} =
result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
proc lenExpr(p: BProc; a: TLoc): Rope =
@@ -326,16 +338,24 @@ template mapTypeChooser(n: PNode): TSymKind =
template mapTypeChooser(a: TLoc): TSymKind = mapTypeChooser(a.lode)
proc addrLoc(conf: ConfigRef; a: TLoc): Rope =
result = a.r
proc addAddrLoc(conf: ConfigRef; a: TLoc; result: var Rope) =
if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a)) != ctArray:
result = "(&" & result & ")"
result.add "(&" & a.r & ")"
else:
result.add a.r
proc addrLoc(conf: ConfigRef; a: TLoc): Rope =
if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a)) != ctArray:
result = "(&" & a.r & ")"
else:
result = a.r
proc byRefLoc(p: BProc; a: TLoc): Rope =
result = a.r
if lfIndirect notin a.flags and mapType(p.config, a.t, mapTypeChooser(a)) != ctArray and not
p.module.compileToCpp:
result = "(&" & result & ")"
result = "(&" & a.r & ")"
else:
result = a.r
proc rdCharLoc(a: TLoc): Rope =
# read a location that may need a char-cast:
@@ -376,8 +396,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: var TLoc,
else:
linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfoV1(p.module, t, a.lode.info)])
of frEmbedded:
# inheritance in C++ does not allow struct initialization: bug #18410
if not p.module.compileToCpp and optTinyRtti in p.config.globalOptions:
if optTinyRtti in p.config.globalOptions:
var tmp: TLoc
if mode == constructRefObj:
let objType = t.skipTypes(abstractInst+{tyRef})
@@ -417,7 +436,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
let typ = skipTypes(loc.t, abstractVarRange)
if isImportedCppType(typ): return
if optSeqDestructors in p.config.globalOptions and typ.kind in {tyString, tySequence}:
assert rdLoc(loc) != nil
assert loc.r != ""
let atyp = skipTypes(loc.t, abstractInst)
if atyp.kind in {tyVar, tyLent}:
@@ -524,7 +543,8 @@ proc getIntTemp(p: BProc, result: var TLoc) =
proc localVarDecl(p: BProc; n: PNode): Rope =
let s = n.sym
if s.loc.k == locNone:
fillLoc(s.loc, locLocalVar, n, mangleLocalName(p, s), OnStack)
fillLocalName(p, s)
fillLoc(s.loc, locLocalVar, n, OnStack)
if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
result.addf("NIM_ALIGN($1) ", [rope(s.alignment)])
@@ -561,7 +581,8 @@ proc treatGlobalDifferentlyForHCR(m: BModule, s: PSym): bool =
proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
let s = n.sym
if s.loc.k == locNone:
fillLoc(s.loc, locGlobalVar, n, mangleName(p.module, s), OnHeap)
fillBackendName(p.module, s)
fillLoc(s.loc, locGlobalVar, n, OnHeap)
if treatGlobalDifferentlyForHCR(p.module, s): incl(s.loc.flags, lfIndirect)
if lfDynamicLib in s.loc.flags:
@@ -570,7 +591,7 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
varInDynamicLib(q, s)
else:
s.loc.r = mangleDynLibProc(s)
if value != nil:
if value != "":
internalError(p.config, n.info, ".dynlib variables cannot have a value")
return
useHeader(p.module, s)
@@ -578,10 +599,10 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
if not containsOrIncl(p.module.declaredThings, s.id):
if sfThread in s.flags:
declareThreadVar(p.module, s, sfImportc in s.flags)
if value != nil:
if value != "":
internalError(p.config, n.info, ".threadvar variables cannot have a value")
else:
var decl: Rope = nil
var decl: Rope = ""
var td = getTypeDesc(p.module, s.loc.t, skVar)
if s.constraint.isNil:
if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
@@ -590,34 +611,35 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
elif sfImportc in s.flags: decl.add("extern ")
elif lfExportLib in s.loc.flags: decl.add("N_LIB_EXPORT_VAR ")
else: decl.add("N_LIB_PRIVATE ")
if s.kind == skLet and value != nil: decl.add("NIM_CONST ")
if s.kind == skLet and value != "": decl.add("NIM_CONST ")
decl.add(td)
if p.hcrOn: decl.add("*")
if sfRegister in s.flags: decl.add(" register")
if sfVolatile in s.flags: decl.add(" volatile")
if sfNoalias in s.flags: decl.add(" NIM_NOALIAS")
if value != nil:
if value != "":
decl.addf(" $1 = $2;$n", [s.loc.r, value])
else:
decl.addf(" $1;$n", [s.loc.r])
else:
if value != nil:
if value != "":
decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.r, value])
else:
decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r])
p.module.s[cfsVars].add(decl)
if p.withinLoop > 0 and value == nil:
if p.withinLoop > 0 and value == "":
# fixes tests/run/tzeroarray:
resetLoc(p, s.loc)
proc assignParam(p: BProc, s: PSym, retType: PType) =
assert(s.loc.r != nil)
assert(s.loc.r != "")
scopeMangledParam(p, s)
proc fillProcLoc(m: BModule; n: PNode) =
let sym = n.sym
if sym.loc.k == locNone:
fillLoc(sym.loc, locProc, n, mangleName(m, sym), OnStack)
fillBackendName(m, sym)
fillLoc(sym.loc, locProc, n, OnStack)
proc getLabel(p: BProc): TLabel =
inc(p.labels)
@@ -632,9 +654,9 @@ proc genStmts(p: BProc, t: PNode)
proc expr(p: BProc, n: PNode, d: var TLoc)
proc genProcPrototype(m: BModule, sym: PSym)
proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc)
proc intLiteral(i: BiggestInt): Rope
proc genLiteral(p: BProc, n: PNode): Rope
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope
proc intLiteral(i: BiggestInt; result: var Rope)
proc genLiteral(p: BProc, n: PNode; result: var Rope)
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope; argsCounter: var int)
proc raiseExit(p: BProc)
proc initLocExpr(p: BProc, e: PNode, result: var TLoc) =
@@ -672,11 +694,11 @@ proc initFrame(p: BProc, procname, filename: Rope): Rope =
if p.module.s[cfsFrameDefines].len == 0:
appcg(p.module, p.module.s[cfsFrameDefines], frameDefines, ["#"])
discard cgsym(p.module, "nimFrame")
cgsym(p.module, "nimFrame")
result = ropecg(p.module, "\tnimfr_($1, $2);$n", [procname, filename])
proc initFrameNoDebug(p: BProc; frame, procname, filename: Rope; line: int): Rope =
discard cgsym(p.module, "nimFrame")
cgsym(p.module, "nimFrame")
p.blocks[0].sections[cpsLocals].addf("TFrame $1;$n", [frame])
result = ropecg(p.module, "\t$1.procname = $2; $1.filename = $3; " &
" $1.line = $4; $1.len = -1; nimFrame(&$1);$n",
@@ -703,24 +725,28 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
if not lib.generated:
lib.generated = true
var tmp = getTempName(m)
assert(lib.name == nil)
assert(lib.name == "")
lib.name = tmp # BUGFIX: cgsym has awful side-effects
m.s[cfsVars].addf("static void* $1;$n", [tmp])
if lib.path.kind in {nkStrLit..nkTripleStrLit}:
var s: TStringSeq = @[]
libCandidates(lib.path.strVal, s)
rawMessage(m.config, hintDependency, lib.path.strVal)
var loadlib: Rope = nil
var loadlib: Rope = ""
for i in 0..high(s):
inc(m.labels)
if i > 0: loadlib.add("||")
let n = newStrNode(nkStrLit, s[i])
n.info = lib.path.info
appcg(m, loadlib, "($1 = #nimLoadLibrary($2))$n",
[tmp, genStringLiteral(m, n)])
appcg(m, loadlib, "($1 = #nimLoadLibrary(", [tmp])
genStringLiteral(m, n, loadlib)
loadlib.addf "))$n", []
appcg(m, m.s[cfsDynLibInit],
"if (!($1)) #nimLoadLibraryError($2);$n",
[loadlib, genStringLiteral(m, lib.path)])
"if (!($1)) #nimLoadLibraryError(",
[loadlib])
genStringLiteral(m, lib.path, m.s[cfsDynLibInit])
m.s[cfsDynLibInit].addf ");$n", []
else:
var p = newProc(nil, m)
p.options.excl optStackTrace
@@ -739,7 +765,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
"if (!($1 = #nimLoadLibrary($2))) #nimLoadLibraryError($2);$n",
[tmp, rdLoc(dest)])
if lib.name == nil: internalError(m.config, "loadDynamicLib")
if lib.name == "": internalError(m.config, "loadDynamicLib")
proc mangleDynLibProc(sym: PSym): Rope =
# we have to build this as a single rope in order not to trip the
@@ -804,18 +830,25 @@ proc symInDynamicLibPartial(m: BModule, sym: PSym) =
sym.loc.r = mangleDynLibProc(sym)
sym.typ.sym = nil # generate a new name
proc cgsym(m: BModule, name: string): Rope =
proc cgsymImpl(m: BModule; sym: PSym) {.inline.} =
case sym.kind
of skProc, skFunc, skMethod, skConverter, skIterator: genProc(m, sym)
of skVar, skResult, skLet: genVarPrototype(m, newSymNode sym)
of skType: discard getTypeDesc(m, sym.typ)
else: internalError(m.config, "cgsym: " & $sym.kind)
proc cgsym(m: BModule, name: string) =
let sym = magicsys.getCompilerProc(m.g.graph, name)
if sym != nil:
case sym.kind
of skProc, skFunc, skMethod, skConverter, skIterator: genProc(m, sym)
of skVar, skResult, skLet: genVarPrototype(m, newSymNode sym)
of skType: discard getTypeDesc(m, sym.typ)
else: internalError(m.config, "cgsym: " & name & ": " & $sym.kind)
cgsymImpl m, sym
else:
rawMessage(m.config, errGenerated, "system module needs: " & name)
proc cgsymValue(m: BModule, name: string): Rope =
let sym = magicsys.getCompilerProc(m.g.graph, name)
if sym != nil:
cgsymImpl m, sym
else:
# we used to exclude the system module from this check, but for DLL
# generation support this sloppyness leads to hard to detect bugs, so
# we're picky here for the system module too:
rawMessage(m.config, errGenerated, "system module needs: " & name)
result = sym.loc.r
if m.hcrOn and sym != nil and sym.kind in {skProc..skIterator}:
@@ -846,12 +879,12 @@ proc generateHeaders(m: BModule) =
#undef unix
""")
proc openNamespaceNim(namespace: string): Rope =
proc openNamespaceNim(namespace: string; result: var Rope) =
result.add("namespace ")
result.add(namespace)
result.add(" {\L")
proc closeNamespaceNim(): Rope =
proc closeNamespaceNim(result: var Rope) =
result.add("}\L")
proc closureSetup(p: BProc, prc: PSym) =
@@ -1029,8 +1062,9 @@ proc isNoReturn(m: BModule; s: PSym): bool {.inline.} =
proc genProcAux(m: BModule, prc: PSym) =
var p = newProc(prc, m)
var header = genProcHeader(m, prc)
var returnStmt: Rope = nil
var header = newRopeAppender()
genProcHeader(m, prc, header)
var returnStmt: Rope = ""
assert(prc.ast != nil)
var procBody = transformBody(m.g.graph, m.idgen, prc, cache = false)
@@ -1052,7 +1086,7 @@ proc genProcAux(m: BModule, prc: PSym) =
else:
# declare the result symbol:
assignLocalVar(p, resNode)
assert(res.loc.r != nil)
assert(res.loc.r != "")
initLocalVar(p, res, immediateAsgn=false)
returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)])
else:
@@ -1110,10 +1144,10 @@ proc genProcAux(m: BModule, prc: PSym) =
if beforeRetNeeded in p.flags: generatedProc.add("{")
generatedProc.add(p.s(cpsInit))
generatedProc.add(p.s(cpsStmts))
if beforeRetNeeded in p.flags: generatedProc.add(~"\t}BeforeRet_: ;$n")
if beforeRetNeeded in p.flags: generatedProc.add("\t}BeforeRet_: ;\n")
if optStackTrace in prc.options: generatedProc.add(deinitFrame(p))
generatedProc.add(returnStmt)
generatedProc.add(~"}$N")
generatedProc.add("}\n")
m.s[cfsProcs].add(generatedProc)
if isReloadable(m, prc):
m.s[cfsDynLibInit].addf("\t$1 = ($3) hcrRegisterProc($4, \"$1\", (void*)$2);$n",
@@ -1142,7 +1176,8 @@ proc genProcPrototype(m: BModule, sym: PSym) =
[mangleDynLibProc(sym), getTypeDesc(m, sym.loc.t), getModuleDllPath(m, sym)])
elif not containsOrIncl(m.declaredProtos, sym.id):
let asPtr = isReloadable(m, sym)
var header = genProcHeader(m, sym, asPtr)
var header = newRopeAppender()
genProcHeader(m, sym, header, asPtr)
if not asPtr:
if isNoReturn(m, sym) and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
header = "__declspec(noreturn) " & header
@@ -1160,7 +1195,7 @@ proc genProcNoForward(m: BModule, prc: PSym) =
fillProcLoc(m, prc.ast[namePos])
useHeader(m, prc)
# dependency to a compilerproc:
discard cgsym(m, prc.name.s)
cgsym(m, prc.name.s)
return
if lfNoDecl in prc.loc.flags:
fillProcLoc(m, prc.ast[namePos])
@@ -1251,14 +1286,15 @@ proc genVarPrototype(m: BModule, n: PNode) =
#assert(sfGlobal in sym.flags)
let sym = n.sym
useHeader(m, sym)
fillLoc(sym.loc, locGlobalVar, n, mangleName(m, sym), OnHeap)
fillBackendName(m, sym)
fillLoc(sym.loc, locGlobalVar, n, OnHeap)
if treatGlobalDifferentlyForHCR(m, sym): incl(sym.loc.flags, lfIndirect)
if (lfNoDecl in sym.loc.flags) or contains(m.declaredThings, sym.id):
return
if sym.owner.id != m.module.id:
# else we already have the symbol generated!
assert(sym.loc.r != nil)
assert(sym.loc.r != "")
if sfThread in sym.flags:
declareThreadVar(m, sym, true)
else:
@@ -1344,9 +1380,11 @@ proc genMainProc(m: BModule) =
assert prc != nil
let n = newStrNode(nkStrLit, prc.annex.path.strVal)
n.info = prc.annex.path.info
var strLit = newRopeAppender()
genStringLiteral(m, n, strLit)
appcg(m, result, "\tif (!($1 = #nimLoadLibrary($2)))$N" &
"\t\t#nimLoadLibraryError($2);$N",
[handle, genStringLiteral(m, n)])
[handle, strLit])
preMainCode.add(loadLib("hcr_handle", "hcrGetProc"))
preMainCode.add("\tvoid* rtl_handle;\L")
@@ -1527,7 +1565,8 @@ proc genMainProc(m: BModule) =
if optNoMain notin m.config.globalOptions:
if m.config.cppCustomNamespace.len > 0:
m.s[cfsProcs].add closeNamespaceNim() & "using namespace " & m.config.cppCustomNamespace & ";\L"
closeNamespaceNim(m.s[cfsProcs])
m.s[cfsProcs].add "using namespace " & m.config.cppCustomNamespace & ";\L"
if m.config.target.targetOS == osWindows and
m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}:
@@ -1551,7 +1590,7 @@ proc genMainProc(m: BModule) =
appcg(m, m.s[cfsProcs], otherMain, [if m.hcrOn: "*" else: "", m.config.nimMainPrefix])
if m.config.cppCustomNamespace.len > 0:
m.s[cfsProcs].add openNamespaceNim(m.config.cppCustomNamespace)
openNamespaceNim(m.config.cppCustomNamespace, m.s[cfsProcs])
proc registerInitProcs*(g: BModuleList; m: PSym; flags: set[ModuleBackendFlag]) =
## Called from the IC backend.
@@ -1734,7 +1773,7 @@ proc genInitCode(m: BModule) =
# Give this small function its own scope
prc.addf("{$N", [])
# Keep a bogus frame in case the code needs one
prc.add(~"\tTFrame FR_; FR_.len = 0;$N")
prc.add("\tTFrame FR_; FR_.len = 0;\n")
writeSection(preInitProc, cpsLocals)
writeSection(preInitProc, cpsInit, m.hcrOn)
@@ -1760,13 +1799,13 @@ proc genInitCode(m: BModule) =
var procname = makeCString(m.module.name.s)
prc.add(initFrame(m.initProc, procname, quotedFilename(m.config, m.module.info)))
else:
prc.add(~"\tTFrame FR_; FR_.len = 0;$N")
prc.add("\tTFrame FR_; FR_.len = 0;\n")
writeSection(initProc, cpsInit, m.hcrOn)
writeSection(initProc, cpsStmts)
if beforeRetNeeded in m.initProc.flags:
prc.add(~"\tBeforeRet_: ;$n")
prc.add("\tBeforeRet_: ;\n")
if sfMainModule in m.module.flags and m.config.exc == excGoto:
if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil:
@@ -1797,7 +1836,7 @@ proc genInitCode(m: BModule) =
m.s[cfsInitProc].addf("}$N$N", [])
for i, el in pairs(m.extensionLoaders):
if el != nil:
if el != "":
let ex = "NIM_EXTERNC N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" %
[(i.ord - '0'.ord).rope, el]
moduleInitRequired = true
@@ -1825,7 +1864,7 @@ proc genModule(m: BModule, cfile: Cfile): Rope =
generateHeaders(m)
result.add(m.s[cfsHeaders])
if m.config.cppCustomNamespace.len > 0:
result.add openNamespaceNim(m.config.cppCustomNamespace)
openNamespaceNim(m.config.cppCustomNamespace, result)
if m.s[cfsFrameDefines].len > 0:
result.add(m.s[cfsFrameDefines])
else:
@@ -1844,10 +1883,10 @@ proc genModule(m: BModule, cfile: Cfile): Rope =
result.add(m.s[cfsDatInitProc])
if m.config.cppCustomNamespace.len > 0:
result.add closeNamespaceNim()
closeNamespaceNim(result)
if moduleIsEmpty:
result = nil
result = ""
proc initProcOptions(m: BModule): TOptions =
let opts = m.config.options
@@ -1868,6 +1907,7 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule
result.typeInfoMarker = initTable[SigHash, Rope]()
result.sigConflicts = initCountTable[SigHash]()
result.initProc = newProc(nil, result)
for i in low(result.s)..high(result.s): result.s[i] = newRopeAppender()
result.initProc.options = initProcOptions(result)
result.preInitProc = newProc(nil, result)
result.preInitProc.flags.incl nimErrorFlagDisabled
@@ -1925,13 +1965,14 @@ proc writeHeader(m: BModule) =
generateThreadLocalStorage(m)
for i in cfsHeaders..cfsProcs:
result.add(m.s[i])
if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders: result.add openNamespaceNim(m.config.cppCustomNamespace)
if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders:
openNamespaceNim(m.config.cppCustomNamespace, result)
result.add(m.s[cfsInitProc])
if optGenDynLib in m.config.globalOptions:
result.add("N_LIB_IMPORT ")
result.addf("N_CDECL(void, $1NimMain)(void);$n", [rope m.config.nimMainPrefix])
if m.config.cppCustomNamespace.len > 0: result.add closeNamespaceNim()
if m.config.cppCustomNamespace.len > 0: closeNamespaceNim(result)
result.addf("#endif /* $1 */$n", [guard])
if not writeRope(result, m.filename):
rawMessage(m.config, errCannotOpenFile, m.filename.string)
@@ -2035,7 +2076,7 @@ proc writeModule(m: BModule, pending: bool) =
var cf = Cfile(nimname: m.module.name.s, cname: cfile,
obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {})
var code = genModule(m, cf)
if code != nil or m.config.symbolFiles != disabledSf:
if code != "" or m.config.symbolFiles != disabledSf:
when hasTinyCBackend:
if m.config.cmd == cmdTcc:
tccgen.compileCCode($code, m.config)
@@ -2061,7 +2102,7 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
# phase ordering problem here: We need to announce this
# dependency to 'nimTestErrorFlag' before system.c has been written to disk.
if m.config.exc == excGoto and getCompilerProc(graph, "nimTestErrorFlag") != nil:
discard cgsym(m, "nimTestErrorFlag")
cgsym(m, "nimTestErrorFlag")
if {optGenStaticLib, optGenDynLib, optNoMain} * m.config.globalOptions == {}:
for i in countdown(high(graph.globalDestructors), 0):
@@ -2077,7 +2118,7 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
if m.hcrOn:
# make sure this is pulled in (meaning hcrGetGlobal() is called for it during init)
discard cgsym(m, "programResult")
cgsym(m, "programResult")
if m.inHcrInitGuard:
endBlock(m.initProc)
@@ -2087,17 +2128,17 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
# so it can load the HCR runtime and later pass the library handle to the HCR runtime which
# will in turn pass it to the other modules it initializes so they can initialize the
# register/get procs so they don't have to have the definitions of these functions as well
discard cgsym(m, "nimLoadLibrary")
discard cgsym(m, "nimLoadLibraryError")
discard cgsym(m, "nimGetProcAddr")
discard cgsym(m, "procAddrError")
discard cgsym(m, "rawWrite")
cgsym(m, "nimLoadLibrary")
cgsym(m, "nimLoadLibraryError")
cgsym(m, "nimGetProcAddr")
cgsym(m, "procAddrError")
cgsym(m, "rawWrite")
# raise dependencies on behalf of genMainProc
if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}:
discard cgsym(m, "initStackBottomWith")
cgsym(m, "initStackBottomWith")
if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
discard cgsym(m, "initThreadVarsEmulation")
cgsym(m, "initThreadVarsEmulation")
if m.g.forwardedProcs.len == 0:
incl m.flags, objHasKidsValid

View File

@@ -27,6 +27,7 @@ type
cfsFieldInfo, # section for field information
cfsTypeInfo, # section for type information (ag ABI checks)
cfsProcHeaders, # section for C procs prototypes
cfsStrData, # section for constant string literals
cfsData, # section for C constant data
cfsVars, # section for C variable declarations
cfsProcs, # section for C procs that are not inline
@@ -190,13 +191,18 @@ proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} =
# top level proc sections
result = p.blocks[0].sections[s]
proc initBlock*(): TBlock =
result = TBlock()
for i in low(result.sections)..high(result.sections):
result.sections[i] = newRopeAppender()
proc newProc*(prc: PSym, module: BModule): BProc =
new(result)
result.prc = prc
result.module = module
result.options = if prc != nil: prc.options
else: module.config.options
newSeq(result.blocks, 1)
result.blocks = @[initBlock()]
result.nestedTryStmts = @[]
result.finallySafePoints = @[]
result.sigConflicts = initCountTable[string]()

View File

@@ -123,7 +123,7 @@ proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
disp.ast = copyTree(s.ast)
disp.ast[bodyPos] = newNodeI(nkEmpty, s.info)
disp.loc.r = nil
disp.loc.r = ""
if s.typ[0] != nil:
if disp.ast.len > resultPos:
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, nextSymId(idgen))

View File

@@ -503,6 +503,17 @@ proc specialDefine(conf: ConfigRef, key: string; pass: TCmdLinePass) =
optOverflowCheck, optAssert, optStackTrace, optLineTrace, optLineDir}
conf.globalOptions.excl {optCDebug}
proc initOrcDefines*(conf: ConfigRef) =
conf.selectedGC = gcOrc
defineSymbol(conf.symbols, "gcorc")
defineSymbol(conf.symbols, "gcdestructors")
incl conf.globalOptions, optSeqDestructors
incl conf.globalOptions, optTinyRtti
defineSymbol(conf.symbols, "nimSeqsV2")
defineSymbol(conf.symbols, "nimV2")
if conf.exc == excNone and conf.backend != backendCpp:
conf.exc = excGoto
proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef, isOrc: bool) =
if isOrc:
conf.selectedGC = gcOrc
@@ -1083,7 +1094,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
processOnOffSwitchG(conf, {optEnableDeepCopy}, arg, pass, info)
of "": # comes from "-" in for example: `nim c -r -` (gets stripped from -)
handleStdinInput(conf)
of "nilseqs", "nilchecks", "mainmodule", "m", "symbol", "taintmode", "cs", "deadcodeelim": warningOptionNoop(switch)
of "nilseqs", "nilchecks", "symbol", "taintmode", "cs", "deadcodeelim": warningOptionNoop(switch)
of "nimmainprefix": conf.nimMainPrefix = arg
else:
if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg)

View File

@@ -143,3 +143,4 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasTopDownInference")
defineSymbol("nimHasTemplateRedefinitionPragma")
defineSymbol("nimHasCstringCase")
defineSymbol("nimHasAmbiguousEnumHint")

View File

@@ -13,7 +13,8 @@ import options, ast, ropes, passes, pathutils, msgs, lineinfos
import modulegraphs
import std/[os, strutils, parseutils]
import std/[os, parseutils]
import strutils except addf
import std/private/globs
when defined(nimPreviewSlimSystem):
@@ -109,7 +110,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext
g.config = graph.config
g.graph = graph
if graph.backend == nil:
graph.backend = Backend(dotGraph: nil)
graph.backend = Backend(dotGraph: "")
result = g
const gendependPass* = makePass(open = myOpen, process = addDotDependency)

View File

@@ -9,27 +9,20 @@
## Data flow analysis for Nim.
## We transform the AST into a linear list of instructions first to
## make this easier to handle: There are only 2 different branching
## make this easier to handle: There are only 3 different branching
## instructions: 'goto X' is an unconditional goto, 'fork X'
## is a conditional goto (either the next instruction or 'X' can be
## taken). Exhaustive case statements are translated
## taken), 'loop X' is the only jump that jumps back.
##
## Exhaustive case statements are translated
## so that the last branch is transformed into an 'else' branch.
## ``return`` and ``break`` are all covered by 'goto'.
##
## Control flow through exception handling:
## Contrary to popular belief, exception handling doesn't cause
## many problems for this DFA representation, ``raise`` is a statement
## that ``goes to`` the outer ``finally`` or ``except`` if there is one,
## otherwise it is the same as ``return``. Every call is treated as
## a call that can potentially ``raise``. However, without a surrounding
## ``try`` we don't emit these ``fork ReturnLabel`` instructions in order
## to speed up the dataflow analysis passes.
##
## The data structures and algorithms used here are inspired by
## "A GraphFree Approach to DataFlow Analysis" by Markus Mohnen.
## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf
import ast, intsets, lineinfos, renderer
import ast, intsets, lineinfos, renderer, aliasanalysis
import std/private/asciitables
when defined(nimPreviewSlimSystem):
@@ -37,12 +30,12 @@ when defined(nimPreviewSlimSystem):
type
InstrKind* = enum
goto, fork, def, use
goto, loop, fork, def, use
Instr* = object
n*: PNode # contains the def/use location.
case kind*: InstrKind
of goto, fork: dest*: int
else: discard
of goto, fork, loop: dest*: int
of def, use:
n*: PNode # contains the def/use location.
ControlFlowGraph* = seq[Instr]
@@ -59,9 +52,10 @@ type
Con = object
code: ControlFlowGraph
inTryStmt: int
inTryStmt, interestingInstructions: int
blocks: seq[TBlock]
owner: PSym
root: PSym
proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
# for debugging purposes
@@ -69,7 +63,7 @@ proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
var jumpTargets = initIntSet()
let last = if last < 0: c.len-1 else: min(last, c.len-1)
for i in start..last:
if c[i].kind in {goto, fork}:
if c[i].kind in {goto, fork, loop}:
jumpTargets.incl(i+c[i].dest)
var i = start
while i <= last:
@@ -80,12 +74,12 @@ proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
case c[i].kind
of def, use:
result.add renderTree(c[i].n)
of goto, fork:
result.add("\t#")
result.add($c[i].n.info.line)
result.add("\n")
of goto, fork, loop:
result.add "L"
result.addInt c[i].dest+i
result.add("\t#")
result.add($c[i].n.info.line)
result.add("\n")
inc i
if i in jumpTargets: result.add("L" & $i & ": End\n")
@@ -93,181 +87,13 @@ proc echoCfg*(c: ControlFlowGraph; start = 0; last = -1) {.deprecated.} =
## echos the ControlFlowGraph for debugging purposes.
echo codeListing(c, start, last).alignTable
proc forkI(c: var Con; n: PNode): TPosition =
proc forkI(c: var Con): TPosition =
result = TPosition(c.code.len)
c.code.add Instr(n: n, kind: fork, dest: 0)
c.code.add Instr(kind: fork, dest: 0)
proc gotoI(c: var Con; n: PNode): TPosition =
proc gotoI(c: var Con): TPosition =
result = TPosition(c.code.len)
c.code.add Instr(n: n, kind: goto, dest: 0)
#[
Join is no more
===============
Instead of generating join instructions we adapt our traversal of the CFG.
When encountering a fork we split into two paths, we follow the path
starting at "pc + 1" until it encounters the joinpoint: "pc + forkInstr.dest".
If we encounter gotos that would jump further than the current joinpoint,
as can happen with gotos generated by unstructured controlflow such as break, raise or return,
we simply suspend following the current path, and follow the other path until the new joinpoint
which is simply the instruction pointer returned to us by the now suspended path.
If the path we are following now, also encounters a goto that exceeds the joinpoint
we repeat the process; suspending the current path and evaluating the other one with a new joinpoint.
If we eventually reach a common joinpoint we join the two paths.
This new "ping-pong" approach has the obvious advantage of not requiring join instructions, as such
cutting down on the CFG size but is also mandatory for correctly handling complicated cases
of unstructured controlflow.
Design of join
==============
block:
if cond: break
def(x)
use(x)
Generates:
L0: fork lab1
join L0 # patched.
goto Louter
lab1:
def x
join L0
Louter:
use x
block outer:
while a:
while b:
if foo:
if bar:
break outer # --> we need to 'join' every pushed 'fork' here
This works and then our abstract interpretation needs to deal with 'fork'
differently. It really causes a split in execution. Two threads are
"spawned" and both need to reach the 'join L' instruction. Afterwards
the abstract interpretations are joined and execution resumes single
threaded.
Abstract Interpretation
-----------------------
proc interpret(pc, state, comesFrom): state =
result = state
# we need an explicit 'create' instruction (an explicit heap), in order
# to deal with 'var x = create(); var y = x; var z = y; destroy(z)'
while true:
case pc
of fork:
let a = interpret(pc+1, result, pc)
let b = interpret(forkTarget, result, pc)
result = a ++ b # ++ is a union operation
inc pc
of join:
if joinTarget == comesFrom: return result
else: inc pc
of use X:
if not result.contains(x):
error "variable not initialized " & x
inc pc
of def X:
if not result.contains(x):
result.incl X
else:
error "overwrite of variable causes memory leak " & x
inc pc
of destroy X:
result.excl X
This is correct but still can lead to false positives:
proc p(cond: bool) =
if cond:
new(x)
otherThings()
if cond:
destroy x
Is not a leak. We should find a way to model *data* flow, not just
control flow. One solution is to rewrite the 'if' without a fork
instruction. The unstructured aspect can now be easily dealt with
the 'goto' and 'join' instructions.
proc p(cond: bool) =
L0: fork Lend
new(x)
# do not 'join' here!
Lend:
otherThings()
join L0 # SKIP THIS FOR new(x) SOMEHOW
destroy x
join L0 # but here.
But if we follow 'goto Louter' we will never come to the join point.
We restore the bindings after popping pc from the stack then there
"no" problem?!
while cond:
prelude()
if not condB: break
postlude()
--->
var setFlag = true
while cond and not setFlag:
prelude()
if not condB:
setFlag = true # BUT: Dependency
if not setFlag: # HERE
postlude()
--->
var setFlag = true
while cond and not setFlag:
prelude()
if not condB:
postlude()
setFlag = true
-------------------------------------------------
while cond:
prelude()
if more:
if not condB: break
stuffHere()
postlude()
-->
var setFlag = true
while cond and not setFlag:
prelude()
if more:
if not condB:
setFlag = false
else:
stuffHere()
postlude()
else:
postlude()
This is getting complicated. Instead we keep the whole 'join' idea but
duplicate the 'join' instructions on breaks and return exits!
]#
c.code.add Instr(kind: goto, dest: 0)
proc genLabel(c: Con): TPosition = TPosition(c.code.len)
@@ -275,8 +101,8 @@ template checkedDistance(dist): int =
doAssert low(int) div 2 + 1 < dist and dist < high(int) div 2
dist
proc jmpBack(c: var Con, n: PNode, p = TPosition(0)) =
c.code.add Instr(n: n, kind: goto, dest: checkedDistance(p.int - c.code.len))
proc jmpBack(c: var Con, p = TPosition(0)) =
c.code.add Instr(kind: loop, dest: checkedDistance(p.int - c.code.len))
proc patch(c: var Con, p: TPosition) =
# patch with current index
@@ -286,12 +112,12 @@ proc gen(c: var Con; n: PNode)
proc popBlock(c: var Con; oldLen: int) =
var exits: seq[TPosition]
exits.add c.gotoI(newNode(nkEmpty))
exits.add c.gotoI()
for f in c.blocks[oldLen].breakFixups:
c.patch(f[0])
for finale in f[1]:
c.gen(finale)
exits.add c.gotoI(newNode(nkEmpty))
exits.add c.gotoI()
for e in exits:
c.patch e
c.blocks.setLen(oldLen)
@@ -306,91 +132,29 @@ proc isTrue(n: PNode): bool =
n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
n.kind == nkIntLit and n.intVal != 0
when true:
proc genWhile(c: var Con; n: PNode) =
# We unroll every loop 3 times. We emulate 0, 1, 2 iterations
# through the loop. We need to prove this is correct for our
# purposes. But Herb Sutter claims it is. (Proof by authority.)
#
# EDIT: Actually, we only need to unroll 2 times
# because Nim doesn't have a way of breaking/goto-ing into
# a loop iteration. Unrolling 2 times is much better for compile
# times of nested loops than 3 times, so we do that here.
#[
while cond:
body
Becomes:
block:
if cond:
body
if cond:
body
if cond:
body
We still need to ensure 'break' resolves properly, so an AST to AST
translation is impossible.
So the code to generate is:
cond
fork L4 # F1
body
cond
fork L5 # F2
body
cond
fork L6 # F3
body
L6:
join F3
L5:
join F2
L4:
join F1
]#
if isTrue(n[0]):
# 'while true' is an idiom in Nim and so we produce
# better code for it:
withBlock(nil):
for i in 0..1:
c.gen(n[1])
else:
withBlock(nil):
var endings: array[2, TPosition]
for i in 0..1:
c.gen(n[0])
endings[i] = c.forkI(n)
c.gen(n[1])
for i in countdown(endings.high, 0):
c.patch(endings[i])
else:
proc genWhile(c: var Con; n: PNode) =
# lab1:
# cond, tmp
# fork tmp, lab2
# body
# jmp lab1
# lab2:
let lab1 = c.genLabel
withBlock(nil):
if isTrue(n[0]):
c.gen(n[1])
c.jmpBack(n, lab1)
else:
c.gen(n[0])
forkT(n):
c.gen(n[1])
c.jmpBack(n, lab1)
template forkT(n, body) =
let lab1 = c.forkI(n)
template forkT(body) =
let lab1 = c.forkI()
body
c.patch(lab1)
proc genWhile(c: var Con; n: PNode) =
# lab1:
# cond, tmp
# fork tmp, lab2
# body
# jmp lab1
# lab2:
let lab1 = c.genLabel
withBlock(nil):
if isTrue(n[0]):
c.gen(n[1])
c.jmpBack(lab1)
else:
c.gen(n[0])
forkT:
c.gen(n[1])
c.jmpBack(lab1)
proc genIf(c: var Con, n: PNode) =
#[
@@ -429,15 +193,22 @@ proc genIf(c: var Con, n: PNode) =
]#
var endings: seq[TPosition] = @[]
let oldInteresting = c.interestingInstructions
let oldLen = c.code.len
for i in 0..<n.len:
let it = n[i]
c.gen(it[0])
if it.len == 2:
forkT(it[1]):
c.gen(it[1])
endings.add c.gotoI(it[1])
for i in countdown(endings.high, 0):
c.patch(endings[i])
forkT:
c.gen(it.lastSon)
endings.add c.gotoI()
if oldInteresting == c.interestingInstructions:
setLen c.code, oldLen
else:
for i in countdown(endings.high, 0):
c.patch(endings[i])
proc genAndOr(c: var Con; n: PNode) =
# asgn dest, a
@@ -446,7 +217,7 @@ proc genAndOr(c: var Con; n: PNode) =
# lab1:
# join F1
c.gen(n[1])
forkT(n):
forkT:
c.gen(n[2])
proc genCase(c: var Con; n: PNode) =
@@ -463,32 +234,32 @@ proc genCase(c: var Con; n: PNode) =
let isExhaustive = skipTypes(n[0].typ,
abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString, tyCstring}
# we generate endings as a set of chained gotos, this is a bit awkward but it
# ensures when recursively traversing the CFG for various analysis, we don't
# artificially extended the life of each branch (for the purposes of DFA)
# beyond the minimum amount.
var endings: seq[TPosition] = @[]
c.gen(n[0])
let oldInteresting = c.interestingInstructions
let oldLen = c.code.len
for i in 1..<n.len:
let it = n[i]
if it.len == 1 or (i == n.len-1 and isExhaustive):
# treat the last branch as 'else' if this is an exhaustive case statement.
c.gen(it.lastSon)
if endings.len != 0:
c.patch(endings[^1])
else:
forkT(it.lastSon):
forkT:
c.gen(it.lastSon)
if endings.len != 0:
c.patch(endings[^1])
endings.add c.gotoI(it.lastSon)
endings.add c.gotoI()
if oldInteresting == c.interestingInstructions:
setLen c.code, oldLen
else:
for i in countdown(endings.high, 0):
c.patch(endings[i])
proc genBlock(c: var Con; n: PNode) =
withBlock(n[0].sym):
c.gen(n[1])
proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
let lab1 = c.gotoI(n)
let lab1 = c.gotoI()
if c.blocks[i].isTryBlock:
c.blocks[i].raiseFixups.add lab1
else:
@@ -501,6 +272,7 @@ proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
c.blocks[i].breakFixups.add (lab1, trailingFinales)
proc genBreak(c: var Con; n: PNode) =
inc c.interestingInstructions
if n[0].kind == nkSym:
for i in countdown(c.blocks.high, 0):
if not c.blocks[i].isTryBlock and c.blocks[i].label == n[0].sym:
@@ -531,9 +303,9 @@ proc genTry(c: var Con; n: PNode) =
for i in 1..<n.len:
let it = n[i]
if it.kind != nkFinally:
forkT(it):
forkT:
c.gen(it.lastSon)
endings.add c.gotoI(it)
endings.add c.gotoI()
for i in countdown(endings.high, 0):
c.patch(endings[i])
@@ -541,11 +313,12 @@ proc genTry(c: var Con; n: PNode) =
if fin.kind == nkFinally:
c.gen(fin[0])
template genNoReturn(c: var Con; n: PNode) =
template genNoReturn(c: var Con) =
# leave the graph
c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
c.code.add Instr(kind: goto, dest: high(int) - c.code.len)
proc genRaise(c: var Con; n: PNode) =
inc c.interestingInstructions
gen(c, n[0])
if c.inTryStmt > 0:
for i in countdown(c.blocks.high, 0):
@@ -554,13 +327,14 @@ proc genRaise(c: var Con; n: PNode) =
return
assert false #Unreachable
else:
genNoReturn(c, n)
genNoReturn(c)
proc genImplicitReturn(c: var Con) =
if c.owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter} and resultPos < c.owner.ast.len:
gen(c, c.owner.ast[resultPos])
proc genReturn(c: var Con; n: PNode) =
inc c.interestingInstructions
if n[0].kind != nkEmpty:
gen(c, n[0])
else:
@@ -569,122 +343,6 @@ proc genReturn(c: var Con; n: PNode) =
const
InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp}
PathKinds0 = {nkDotExpr, nkCheckedFieldExpr,
nkBracketExpr, nkDerefExpr, nkHiddenDeref,
nkAddr, nkHiddenAddr,
nkObjDownConv, nkObjUpConv}
PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv}
proc skipConvDfa*(n: PNode): PNode =
result = n
while true:
case result.kind
of nkObjDownConv, nkObjUpConv:
result = result[0]
of PathKinds1:
result = result[1]
else: break
type AliasKind* = enum
yes, no, maybe
proc aliases*(obj, field: PNode): AliasKind =
# obj -> field:
# x -> x: true
# x -> x.f: true
# x.f -> x: false
# x.f -> x.f: true
# x.f -> x.v: false
# x -> x[0]: true
# x[0] -> x: false
# x[0] -> x[0]: true
# x[0] -> x[1]: false
# x -> x[i]: true
# x[i] -> x: false
# x[i] -> x[i]: maybe; Further analysis could make this return true when i is a runtime-constant
# x[i] -> x[j]: maybe; also returns maybe if only one of i or j is a compiletime-constant
template collectImportantNodes(result, n) =
var result: seq[PNode]
var n = n
while true:
case n.kind
of PathKinds0 - {nkDotExpr, nkBracketExpr}:
n = n[0]
of PathKinds1:
n = n[1]
of nkDotExpr, nkBracketExpr:
result.add n
n = n[0]
of nkSym:
result.add n; break
else: return no
collectImportantNodes(objImportantNodes, obj)
collectImportantNodes(fieldImportantNodes, field)
# If field is less nested than obj, then it cannot be part of/aliased by obj
if fieldImportantNodes.len < objImportantNodes.len: return no
result = yes
for i in 1..objImportantNodes.len:
# We compare the nodes leading to the location of obj and field
# with each other.
# We continue until they diverge, in which case we return no, or
# until we reach the location of obj, in which case we do not need
# to look further, since field must be part of/aliased by obj now.
# If we encounter an element access using an index which is a runtime value,
# we simply return maybe instead of yes; should further nodes not diverge.
let currFieldPath = fieldImportantNodes[^i]
let currObjPath = objImportantNodes[^i]
if currFieldPath.kind != currObjPath.kind:
return no
case currFieldPath.kind
of nkSym:
if currFieldPath.sym != currObjPath.sym: return no
of nkDotExpr:
if currFieldPath[1].sym != currObjPath[1].sym: return no
of nkBracketExpr:
if currFieldPath[1].kind in nkLiterals and currObjPath[1].kind in nkLiterals:
if currFieldPath[1].intVal != currObjPath[1].intVal:
return no
else:
result = maybe
else: assert false # unreachable
proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
var n = orig
while true:
case n.kind
of PathKinds0 - {nkHiddenDeref, nkDerefExpr}:
n = n[0]
of PathKinds1:
n = n[1]
of nkHiddenDeref, nkDerefExpr:
# We "own" sinkparam[].loc but not ourVar[].location as it is a nasty
# pointer indirection.
# bug #14159, we cannot reason about sinkParam[].location as it can
# still be shared for tyRef.
n = n[0]
return n.kind == nkSym and n.sym.owner == owner and
(n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned})
else: break
# XXX Allow closure deref operations here if we know
# the owner controlled the closure allocation?
result = n.kind == nkSym and n.sym.owner == owner and
{sfGlobal, sfThread, sfCursor} * n.sym.flags == {} and
(n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar)
# Note: There is a different move analyzer possible that checks for
# consume(param.key); param.key = newValue for all paths. Then code like
#
# let splited = split(move self.root, x)
# self.root = merge(splited.lower, splited.greater)
#
# could be written without the ``move self.root``. However, this would be
# wrong! Then the write barrier for the ``self.root`` assignment would
# free the old data and all is lost! Lesson: Don't be too smart, trust the
# lower level C++ optimizer to specialize this code.
proc skipTrivials(c: var Con, n: PNode): PNode =
result = n
@@ -703,8 +361,9 @@ proc genUse(c: var Con; orig: PNode) =
let n = c.skipTrivials(orig)
if n.kind == nkSym:
if n.sym.kind in InterestingSyms:
c.code.add Instr(n: orig, kind: use)
if n.sym.kind in InterestingSyms and n.sym == c.root:
c.code.add Instr(kind: use, n: orig)
inc c.interestingInstructions
else:
gen(c, n)
@@ -712,7 +371,9 @@ proc genDef(c: var Con; orig: PNode) =
let n = c.skipTrivials(orig)
if n.kind == nkSym and n.sym.kind in InterestingSyms:
c.code.add Instr(n: orig, kind: def)
if n.sym == c.root:
c.code.add Instr(kind: def, n: orig)
inc c.interestingInstructions
proc genCall(c: var Con; n: PNode) =
gen(c, n[0])
@@ -725,13 +386,13 @@ proc genCall(c: var Con; n: PNode) =
# Pass by 'out' is a 'must def'. Good enough for a move optimizer.
genDef(c, n[i])
# every call can potentially raise:
if c.inTryStmt > 0 and canRaiseConservative(n[0]):
if false: # c.inTryStmt > 0 and canRaiseConservative(n[0]):
# we generate the instruction sequence:
# fork lab1
# goto exceptionHandler (except or finally)
# lab1:
# join F1
forkT(n):
forkT:
for i in countdown(c.blocks.high, 0):
if c.blocks[i].isTryBlock:
genBreakOrRaiseAux(c, i, n)
@@ -769,7 +430,7 @@ proc gen(c: var Con; n: PNode) =
else:
genCall(c, n)
if sfNoReturn in n[0].sym.flags:
genNoReturn(c, n)
genNoReturn(c)
else:
genCall(c, n)
of nkCharLit..nkNilLit: discard
@@ -810,13 +471,32 @@ proc gen(c: var Con; n: PNode) =
of nkDefer: doAssert false, "dfa construction pass requires the elimination of 'defer'"
else: discard
proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph =
when false:
proc optimizeJumps(c: var ControlFlowGraph) =
for i in 0..<c.len:
case c[i].kind
of goto, fork:
var pc = i + c[i].dest
if pc < c.len and c[pc].kind == goto:
while pc < c.len and c[pc].kind == goto:
let newPc = pc + c[pc].dest
if newPc > pc:
pc = newPc
else:
break
c[i].dest = pc - i
of loop, def, use: discard
proc constructCfg*(s: PSym; body: PNode; root: PSym): ControlFlowGraph =
## constructs a control flow graph for ``body``.
var c = Con(code: @[], blocks: @[], owner: s)
var c = Con(code: @[], blocks: @[], owner: s, root: root)
withBlock(s):
gen(c, body)
genImplicitReturn(c)
if root.kind == skResult:
genImplicitReturn(c)
when defined(gcArc) or defined(gcOrc):
result = c.code # will move
else:
shallowCopy(result, c.code)
when false:
optimizeJumps result

View File

@@ -304,6 +304,10 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
conf.configVars, filename.string,
docgenFindFile, compilerMsgHandler)
if conf.configVars.hasKey("doc.googleAnalytics") and
conf.configVars.hasKey("doc.plausibleAnalytics"):
doAssert false, "Either use googleAnalytics or plausibleAnalytics"
if conf.configVars.hasKey("doc.googleAnalytics"):
result.analytics = """
<script>
@@ -317,6 +321,10 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
</script>
""" % [conf.configVars.getOrDefault"doc.googleAnalytics"]
elif conf.configVars.hasKey("doc.plausibleAnalytics"):
result.analytics = """
<script defer data-domain="$1" src="https://plausible.io/js/plausible.js"></script>
""" % [conf.configVars.getOrDefault"doc.plausibleAnalytics"]
else:
result.analytics = ""

View File

@@ -14,7 +14,9 @@
import ropes, platform, condsyms, options, msgs, lineinfos, pathutils, modulepaths
import std/[os, strutils, osproc, sha1, streams, sequtils, times, strtabs, json, jsonutils, sugar, parseutils]
import std/[os, osproc, sha1, streams, sequtils, times, strtabs, json, jsonutils, sugar, parseutils]
import std / strutils except addf
when defined(nimPreviewSlimSystem):
import std/syncio
@@ -89,7 +91,7 @@ compiler gcc:
asmStmtFrmt: "__asm__($1);$n",
structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
produceAsm: gnuAsmListing,
cppXsupport: "-std=gnu++14 -funsigned-char",
cppXsupport: "-std=gnu++17 -funsigned-char",
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
hasAttribute})
@@ -116,7 +118,7 @@ compiler nintendoSwitchGCC:
asmStmtFrmt: "asm($1);$n",
structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
produceAsm: gnuAsmListing,
cppXsupport: "-std=gnu++14 -funsigned-char",
cppXsupport: "-std=gnu++17 -funsigned-char",
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
hasAttribute})
@@ -153,7 +155,7 @@ compiler vcc:
compileTmpl: "/c$vccplatform $options $include /nologo /Fo$objfile $file",
buildGui: " /SUBSYSTEM:WINDOWS user32.lib ",
buildDll: " /LD",
buildLib: "lib /OUT:$libfile $objfiles",
buildLib: "vccexe --command:lib$vccplatform /nologo /OUT:$libfile $objfiles",
linkerExe: "cl",
linkTmpl: "$builddll$vccplatform /Fe$exefile $objfiles $buildgui /nologo $options",
includeCmd: " /I",
@@ -616,7 +618,7 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile,
"for the selected C compiler: " & CC[conf.cCompiler].name)
result.add(' ')
result.addf(CC[c].compileTmpl, [
strutils.addf(result, CC[c].compileTmpl, [
"dfile", dfile,
"file", cfsh, "objfile", quoteShell(objfile),
"options", options, "include", includeCmd,
@@ -674,7 +676,8 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
if removeStaticFile:
removeFile output # fixes: bug #16947
result = CC[conf.cCompiler].buildLib % ["libfile", quoteShell(output),
"objfiles", objfiles]
"objfiles", objfiles,
"vccplatform", vccplatform(conf)]
else:
var linkerExe = getConfigVar(conf, conf.cCompiler, ".linkerexe")
if linkerExe.len == 0: linkerExe = getLinkerExe(conf, conf.cCompiler)
@@ -707,7 +710,7 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
"exefile", exefile, "nim", getPrefixDir(conf).string, "lib", conf.libpath.string])
result.add ' '
result.addf(linkTmpl, ["builddll", builddll,
strutils.addf(result, linkTmpl, ["builddll", builddll,
"mapfile", mapfile,
"buildgui", buildgui, "options", linkOptions,
"objfiles", objfiles, "exefile", exefile,
@@ -856,7 +859,7 @@ proc callCCompiler*(conf: ConfigRef) =
return # speed up that call if only compiling and no script shall be
# generated
#var c = cCompiler
var script: Rope = nil
var script: Rope = ""
var cmds: TStringSeq
var prettyCmds: TStringSeq
let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])

View File

@@ -200,7 +200,7 @@ proc highBound*(conf: ConfigRef; x: PNode; o: Operators): PNode =
nkIntLit.newIntNode(lastOrd(conf, typ))
elif typ.kind == tySequence and x.kind == nkSym and
x.sym.kind == skConst:
nkIntLit.newIntNode(x.sym.ast.len-1)
nkIntLit.newIntNode(x.sym.astdef.len-1)
else:
o.opAdd.buildCall(o.opLen.buildCall(x), minusOne())
result.info = x.info

View File

@@ -403,7 +403,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
p.bitsize = s.bitsize
p.alignment = s.alignment
p.externalName = toLitId(if s.loc.r.isNil: "" else: $s.loc.r, m)
p.externalName = toLitId(s.loc.r, m)
p.locFlags = s.loc.flags
c.addMissing s.typ
p.typ = s.typ.storeType(c, m)

View File

@@ -15,9 +15,9 @@
import
intsets, strtabs, ast, astalgo, msgs, renderer, magicsys, types, idents,
strutils, options, dfa, lowerings, tables, modulegraphs,
strutils, options, lowerings, tables, modulegraphs,
lineinfos, parampatterns, sighashes, liftdestructors, optimizer,
varpartitions
varpartitions, aliasanalysis, dfa
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -27,12 +27,14 @@ from trees import exprStructuralEquivalent, getRoot
type
Con = object
owner: PSym
g: ControlFlowGraph
when true:
g: ControlFlowGraph
graph: ModuleGraph
inLoop, inSpawn, inLoopCond: int
uninit: IntSet # set of uninit'ed vars
uninitComputed: bool
idgen: IdGenerator
body: PNode
otherUsage: TLineInfo
Scope = object # we do scope-based memory management.
# a scope is comparable to an nkStmtListExpr like
@@ -40,6 +42,8 @@ type
vars: seq[PSym]
wasMoved: seq[PNode]
final: seq[PNode] # finally section
locals: seq[PSym]
body: PNode
needsTry: bool
parent: ptr Scope
@@ -70,160 +74,95 @@ proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode =
s.vars.add(sym)
result = newSymNode(sym)
proc nestedScope(parent: var Scope): Scope =
Scope(vars: @[], wasMoved: @[], final: @[], needsTry: false, parent: addr(parent))
proc nestedScope(parent: var Scope; body: PNode): Scope =
Scope(vars: @[], locals: @[], wasMoved: @[], final: @[], body: body, needsTry: false, parent: addr(parent))
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; isDecl = false): PNode
import sets, hashes
when false:
var
perfCounters: array[InstrKind, int]
proc hash(n: PNode): Hash = hash(cast[pointer](n))
proc showCounters*() =
for i in low(InstrKind)..high(InstrKind):
echo "INSTR ", i, " ", perfCounters[i]
type
State = ref object
lastReads: IntSet
potentialLastReads: IntSet
notLastReads: IntSet
alreadySeen: HashSet[PNode]
proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
let root = parampatterns.exprRoot(n, allowCalls=false)
if root == nil: return false
proc preprocessCfg(cfg: var ControlFlowGraph) =
for i in 0..<cfg.len:
if cfg[i].kind in {goto, fork} and i + cfg[i].dest > cfg.len:
cfg[i].dest = cfg.len - i
var s = addr(scope)
while s != nil:
if s.locals.contains(root): break
s = s.parent
proc mergeStates(a: var State, b: sink State) =
# Inplace for performance:
# lastReads = a.lastReads + b.lastReads
# potentialLastReads = (a.potentialLastReads + b.potentialLastReads) - (a.notLastReads + b.notLastReads)
# notLastReads = a.notLastReads + b.notLastReads
# alreadySeen = a.alreadySeen + b.alreadySeen
# b is never nil
if a == nil:
a = b
c.g = constructCfg(c.owner, if s != nil: s.body else: c.body, root)
dbg:
echo "\n### ", c.owner.name.s, ":\nCFG:"
echoCfg(c.g)
#echo c.body
var j = 0
while j < c.g.len:
if c.g[j].kind == use and c.g[j].n == n: break
inc j
c.otherUsage = unknownLineInfo
if j < c.g.len:
var pcs = @[j+1]
var marked = initIntSet()
result = true
while pcs.len > 0:
var pc = pcs.pop()
if not marked.contains(pc):
let oldPc = pc
while pc < c.g.len:
dbg:
echo "EXEC ", c.g[pc].kind, " ", pc, " ", n
when false:
inc perfCounters[c.g[pc].kind]
case c.g[pc].kind
of loop:
let back = pc + c.g[pc].dest
if not marked.containsOrIncl(back):
pc = back
else:
break
of goto:
pc = pc + c.g[pc].dest
of fork:
if not marked.contains(pc+1):
pcs.add pc + 1
pc = pc + c.g[pc].dest
of use:
if c.g[pc].n.aliases(n) != no or n.aliases(c.g[pc].n) != no:
c.otherUsage = c.g[pc].n.info
return false
inc pc
of def:
if c.g[pc].n.aliases(n) == yes:
# the path leads to a redefinition of 's' --> sink 's'.
break
elif n.aliases(c.g[pc].n) != no:
# only partially writes to 's' --> can't sink 's', so this def reads 's'
# or maybe writes to 's' --> can't sink 's'
c.otherUsage = c.g[pc].n.info
return false
inc pc
marked.incl oldPc
else:
a.lastReads.incl b.lastReads
a.potentialLastReads.incl b.potentialLastReads
a.potentialLastReads.excl a.notLastReads
a.potentialLastReads.excl b.notLastReads
a.notLastReads.incl b.notLastReads
a.alreadySeen.incl b.alreadySeen
result = false
proc computeLastReadsAndFirstWrites(cfg: ControlFlowGraph) =
template aliasesCached(obj, field: PNode): AliasKind =
aliases(obj, field)
proc isLastRead(n: PNode; c: var Con; s: var Scope): bool =
if not hasDestructor(c, n.typ): return true
var cfg = cfg
preprocessCfg(cfg)
var states = newSeq[State](cfg.len + 1)
states[0] = State()
for pc in 0..<cfg.len:
template state: State = states[pc]
if state != nil:
case cfg[pc].kind
of def:
var potentialLastReadsCopy = state.potentialLastReads
for r in potentialLastReadsCopy:
if cfg[pc].n.aliasesCached(cfg[r].n) == yes:
# the path leads to a redefinition of 's' --> sink 's'.
state.lastReads.incl r
state.potentialLastReads.excl r
elif cfg[r].n.aliasesCached(cfg[pc].n) != no:
# only partially writes to 's' --> can't sink 's', so this def reads 's'
# or maybe writes to 's' --> can't sink 's'
cfg[r].n.comment = '\n' & $pc
state.potentialLastReads.excl r
state.notLastReads.incl r
var alreadySeenThisNode = false
for s in state.alreadySeen:
if cfg[pc].n.aliasesCached(s) != no or s.aliasesCached(cfg[pc].n) != no:
alreadySeenThisNode = true; break
if alreadySeenThisNode: cfg[pc].n.flags.excl nfFirstWrite
else: cfg[pc].n.flags.incl nfFirstWrite
state.alreadySeen.incl cfg[pc].n
mergeStates(states[pc + 1], move(states[pc]))
of use:
var potentialLastReadsCopy = state.potentialLastReads
for r in potentialLastReadsCopy:
if cfg[pc].n.aliasesCached(cfg[r].n) != no or cfg[r].n.aliasesCached(cfg[pc].n) != no:
cfg[r].n.comment = '\n' & $pc
state.potentialLastReads.excl r
state.notLastReads.incl r
state.potentialLastReads.incl pc
state.alreadySeen.incl cfg[pc].n
mergeStates(states[pc + 1], move(states[pc]))
of goto:
mergeStates(states[pc + cfg[pc].dest], move(states[pc]))
of fork:
var copy = State()
copy[] = states[pc][]
mergeStates(states[pc + cfg[pc].dest], copy)
mergeStates(states[pc + 1], move(states[pc]))
let lastReads = (states[^1].lastReads + states[^1].potentialLastReads) - states[^1].notLastReads
var lastReadTable: Table[PNode, seq[int]]
for position, node in cfg:
if node.kind == use:
lastReadTable.mgetOrPut(node.n, @[]).add position
for node, positions in lastReadTable:
block checkIfAllPosLastRead:
for p in positions:
if p notin lastReads: break checkIfAllPosLastRead
node.flags.incl nfLastRead
proc isLastRead(n: PNode; c: var Con): bool =
let m = dfa.skipConvDfa(n)
(m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or nfLastRead in m.flags
let m = skipConvDfa(n)
result = (m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or
isLastReadImpl(n, c, s)
proc isFirstWrite(n: PNode; c: var Con): bool =
let m = dfa.skipConvDfa(n)
nfFirstWrite in m.flags
proc initialized(code: ControlFlowGraph; pc: int,
init, uninit: var IntSet; until: int): int =
## Computes the set of definitely initialized variables across all code paths
## as an IntSet of IDs.
var pc = pc
while pc < code.len:
case code[pc].kind
of goto:
pc += code[pc].dest
of fork:
var initA = initIntSet()
var initB = initIntSet()
var variantA = pc + 1
var variantB = pc + code[pc].dest
while variantA != variantB:
if max(variantA, variantB) > until:
break
if variantA < variantB:
variantA = initialized(code, variantA, initA, uninit, min(variantB, until))
else:
variantB = initialized(code, variantB, initB, uninit, min(variantA, until))
pc = min(variantA, variantB)
# we add vars if they are in both branches:
for v in initA:
if v in initB:
init.incl v
of use:
let v = code[pc].n.sym
if v.kind != skParam and v.id notin init:
# attempt to read an uninit'ed variable
uninit.incl v.id
inc pc
of def:
let v = code[pc].n.sym
init.incl v.id
inc pc
return pc
let m = skipConvDfa(n)
result = nfFirstWrite2 in m.flags
proc isCursor(n: PNode): bool =
case n.kind
@@ -247,9 +186,11 @@ proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) =
m.add "; requires a copy because it's not the last read of '"
m.add renderTree(ri)
m.add '\''
if ri.comment.startsWith('\n'):
if c.otherUsage != unknownLineInfo:
# ri.comment.startsWith('\n'):
m.add "; another read is done here: "
m.add c.graph.config $ c.g[parseInt(ri.comment[1..^1])].n.info
m.add c.graph.config $ c.otherUsage
#m.add c.graph.config $ c.g[parseInt(ri.comment[1..^1])].n.info
elif ri.kind == nkSym and ri.sym.kind == skParam and not isSinkType(ri.sym.typ):
m.add "; try to make "
m.add renderTree(ri)
@@ -625,7 +566,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
var branch = shallowCopy(it)
for j in 0 ..< it.len-1:
branch[j] = copyTree(it[j])
var ofScope = nestedScope(s)
var ofScope = nestedScope(s, it.lastSon)
branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
processScope(c, ofScope, maybeVoid(it[^1], ofScope))
else:
@@ -638,7 +579,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
result = copyNode(n)
result.add p(n[0], c, s, normal)
dec c.inLoopCond
var bodyScope = nestedScope(s)
var bodyScope = nestedScope(s, n[1])
let bodyResult = p(n[1], c, bodyScope, normal)
result.add processScope(c, bodyScope, bodyResult)
dec c.inLoop
@@ -650,7 +591,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
for i in 0..<last-1:
result[i] = n[i]
result[last-1] = p(n[last-1], c, s, normal)
var bodyScope = nestedScope(s)
var bodyScope = nestedScope(s, n[1])
let bodyResult = p(n[last], c, bodyScope, normal)
result[last] = processScope(c, bodyScope, bodyResult)
dec c.inLoop
@@ -658,7 +599,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
of nkBlockStmt, nkBlockExpr:
result = copyNode(n)
result.add n[0]
var bodyScope = nestedScope(s)
var bodyScope = nestedScope(s, n[1])
result.add if n[1].typ.isEmptyType or willProduceStmt:
processScope(c, bodyScope, processCall(n[1], bodyScope))
else:
@@ -669,7 +610,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
for i in 0..<n.len:
let it = n[i]
var branch = shallowCopy(it)
var branchScope = nestedScope(s)
var branchScope = nestedScope(s, it.lastSon)
if it.kind in {nkElifBranch, nkElifExpr}:
#Condition needs to be destroyed outside of the condition/branch scope
branch[0] = p(it[0], c, s, normal)
@@ -682,7 +623,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
of nkTryStmt:
result = copyNode(n)
var tryScope = nestedScope(s)
var tryScope = nestedScope(s, n[0])
result.add if n[0].typ.isEmptyType or willProduceStmt:
processScope(c, tryScope, maybeVoid(n[0], tryScope))
else:
@@ -691,7 +632,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
for i in 1..<n.len:
let it = n[i]
var branch = copyTree(it)
var branchScope = nestedScope(s)
var branchScope = nestedScope(s, it[^1])
branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt or it.kind == nkFinally:
processScope(c, branchScope, if it.kind == nkFinally: p(it[^1], c, branchScope, normal)
else: maybeVoid(it[^1], branchScope))
@@ -744,7 +685,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
nkCallKinds + nkLiterals:
result = p(n, c, s, consumed)
elif ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and
isLastRead(n, c) and not (n.kind == nkSym and isCursor(n)):
isLastRead(n, c, s) and not (n.kind == nkSym and isCursor(n)):
# Sinked params can be consumed only once. We need to reset the memory
# to disable the destructor which we have not elided
result = destructiveMoveVar(n, c, s)
@@ -864,13 +805,16 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
for it in n:
var ri = it[^1]
if it.kind == nkVarTuple and hasDestructor(c, ri.typ):
for i in 0..<it.len-2:
if it[i].kind == nkSym: s.locals.add it[i].sym
let x = lowerTupleUnpacking(c.graph, it, c.idgen, c.owner)
result.add p(x, c, s, consumed)
elif it.kind == nkIdentDefs and hasDestructor(c, it[0].typ):
elif it.kind == nkIdentDefs and hasDestructor(c, skipPragmaExpr(it[0]).typ):
for j in 0..<it.len-2:
let v = it[j]
let v = skipPragmaExpr(it[j])
if v.kind == nkSym:
if sfCompileTime in v.sym.flags: continue
s.locals.add v.sym
pVarTopLevel(v, c, s, result)
if ri.kind != nkEmpty:
result.add moveOrCopy(v, ri, c, s, isDecl = v.kind == nkSym)
@@ -943,7 +887,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
for i in 1 ..< n.len:
result[i] = n[i]
if mode == sinkArg and hasDestructor(c, n.typ):
if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c):
if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c, s):
s.wasMoved.add c.genWasMoved(n)
else:
result = passCopyToSink(result, c, s)
@@ -953,7 +897,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
for i in 0 ..< n.len:
result[i] = p(n[i], c, s, normal)
if mode == sinkArg and hasDestructor(c, n.typ):
if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c):
if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c, s):
# consider 'a[(g; destroy(g); 3)]', we want to say 'wasMoved(a[3])'
# without the junk, hence 'c.genWasMoved(n)'
# and not 'c.genWasMoved(result)':
@@ -1054,7 +998,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNod
if isUnpackedTuple(ri[0]):
# unpacking of tuple: take over the elements
result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c):
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s):
if aliases(dest, ri) == no:
# Rule 3: `=sink`(x, z); wasMoved(z)
if isAtom(ri[1]):
@@ -1079,12 +1023,12 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNod
of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
of nkSym:
if isSinkParam(ri.sym) and isLastRead(ri, c):
if isSinkParam(ri.sym) and isLastRead(ri, c, s):
# Rule 3: `=sink`(x, z); wasMoved(z)
let snk = c.genSink(dest, ri, isDecl)
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
elif ri.sym.kind != skParam and ri.sym.owner == c.owner and
isLastRead(ri, c) and canBeMoved(c, dest.typ) and not isCursor(ri):
isLastRead(ri, c, s) and canBeMoved(c, dest.typ) and not isCursor(ri):
# Rule 3: `=sink`(x, z); wasMoved(z)
let snk = c.genSink(dest, ri, isDecl)
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
@@ -1101,7 +1045,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNod
of nkRaiseStmt:
result = pRaiseStmt(ri, c, s)
else:
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
canBeMoved(c, dest.typ):
# Rule 3: `=sink`(x, z); wasMoved(z)
let snk = c.genSink(dest, ri, isDecl)
@@ -1111,49 +1055,44 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNod
result.add p(ri, c, s, consumed)
c.finishCopy(result, dest, isFromSink = false)
proc computeUninit(c: var Con) =
if not c.uninitComputed:
c.uninitComputed = true
c.uninit = initIntSet()
var init = initIntSet()
discard initialized(c.g, pc = 0, init, c.uninit, int.high)
when false:
proc computeUninit(c: var Con) =
if not c.uninitComputed:
c.uninitComputed = true
c.uninit = initIntSet()
var init = initIntSet()
discard initialized(c.g, pc = 0, init, c.uninit, int.high)
proc injectDefaultCalls(n: PNode, c: var Con) =
case n.kind
of nkVarSection, nkLetSection:
for it in n:
if it.kind == nkIdentDefs and it[^1].kind == nkEmpty:
computeUninit(c)
for j in 0..<it.len-2:
let v = it[j]
doAssert v.kind == nkSym
if c.uninit.contains(v.sym.id):
it[^1] = genDefaultCall(v.sym.typ, c, v.info)
break
of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef,
nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
discard
else:
for i in 0..<n.safeLen:
injectDefaultCalls(n[i], c)
proc injectDefaultCalls(n: PNode, c: var Con) =
case n.kind
of nkVarSection, nkLetSection:
for it in n:
if it.kind == nkIdentDefs and it[^1].kind == nkEmpty:
computeUninit(c)
for j in 0..<it.len-2:
let v = skipPragmaExpr(it[j])
doAssert v.kind == nkSym
if c.uninit.contains(v.sym.id):
it[^1] = genDefaultCall(v.sym.typ, c, v.info)
break
of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef,
nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
discard
else:
for i in 0..<n.safeLen:
injectDefaultCalls(n[i], c)
proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): PNode =
when toDebug.len > 0:
shouldDebug = toDebug == owner.name.s or toDebug == "always"
if sfGeneratedOp in owner.flags or (owner.kind == skIterator and isInlineIterator(owner.typ)):
return n
var c = Con(owner: owner, graph: g, g: constructCfg(owner, n), idgen: idgen)
dbg:
echo "\n### ", owner.name.s, ":\nCFG:"
echoCfg(c.g)
echo n
var c = Con(owner: owner, graph: g, idgen: idgen, body: n, otherUsage: unknownLineInfo)
if optCursorInference in g.config.options:
computeCursors(owner, n, g)
computeLastReadsAndFirstWrites(c.g)
var scope: Scope
var scope = Scope(body: n)
let body = p(n, c, scope, normal)
if owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}:

View File

@@ -57,15 +57,9 @@ proc toInt128*[T: SomeInteger | bool](arg: T): Int128 =
template isNegative(arg: Int128): bool =
arg.sdata(3) < 0
template isNegative(arg: int32): bool =
arg < 0
proc bitconcat(a, b: uint32): uint64 =
(uint64(a) shl 32) or uint64(b)
proc bitsplit(a: uint64): (uint32, uint32) =
(cast[uint32](a shr 32), cast[uint32](a))
proc toInt64*(arg: Int128): int64 =
if isNegative(arg):
assert(arg.sdata(3) == -1, "out of range")
@@ -211,12 +205,6 @@ proc `==`*(a, b: Int128): bool =
if a.udata[3] != b.udata[3]: return false
return true
proc inplaceBitnot(a: var Int128) =
a.udata[0] = not a.udata[0]
a.udata[1] = not a.udata[1]
a.udata[2] = not a.udata[2]
a.udata[3] = not a.udata[3]
proc bitnot*(a: Int128): Int128 =
result.udata[0] = not a.udata[0]
result.udata[1] = not a.udata[1]
@@ -357,18 +345,6 @@ proc low64(a: Int128): uint64 =
bitconcat(a.udata[1], a.udata[0])
proc `*`*(lhs, rhs: Int128): Int128 =
let
a = cast[uint64](lhs.udata[0])
b = cast[uint64](lhs.udata[1])
c = cast[uint64](lhs.udata[2])
d = cast[uint64](lhs.udata[3])
e = cast[uint64](rhs.udata[0])
f = cast[uint64](rhs.udata[1])
g = cast[uint64](rhs.udata[2])
h = cast[uint64](rhs.udata[3])
let a32 = cast[uint64](lhs.udata[1])
let a00 = cast[uint64](lhs.udata[0])
let b32 = cast[uint64](rhs.udata[1])
@@ -444,11 +420,11 @@ proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
result.remainder = dividend
proc `div`*(a, b: Int128): Int128 =
let (a, b) = divMod(a, b)
let (a, _) = divMod(a, b)
return a
proc `mod`*(a, b: Int128): Int128 =
let (a, b) = divMod(a, b)
let (_, b) = divMod(a, b)
return b
proc addInt128*(result: var string; value: Int128) =

View File

@@ -35,7 +35,8 @@ import
cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils,
transf, injectdestructors, sourcemap, astmsgs
import json, sets, math, tables, intsets, strutils
import json, sets, math, tables, intsets
import strutils except addf
when defined(nimPreviewSlimSystem):
import std/[assertions, syncio]
@@ -110,21 +111,18 @@ type
template config*(p: PProc): ConfigRef = p.module.config
proc indentLine(p: PProc, r: Rope): Rope =
result = r
var p = p
var ind = 0
while true:
for i in 0..<p.blocks.len + p.extraIndent:
prepend(result, rope" ")
inc ind, p.blocks.len + p.extraIndent
if p.up == nil or p.up.prc != p.prc.owner:
break
p = p.up
result = repeat(' ', ind*2) & r
template line(p: PProc, added: string) =
p.body.add(indentLine(p, rope(added)))
template line(p: PProc, added: Rope) =
p.body.add(indentLine(p, added))
template lineF(p: PProc, frmt: FormatStr, args: varargs[Rope]) =
p.body.add(indentLine(p, ropes.`%`(frmt, args)))
@@ -140,9 +138,9 @@ proc newGlobals(): PGlobals =
result.typeInfoGenerated = initIntSet()
proc initCompRes(r: var TCompRes) =
r.address = nil
r.res = nil
r.tmpLoc = nil
r.address = ""
r.res = ""
r.tmpLoc = ""
r.typ = etyNone
r.kind = resNone
@@ -241,7 +239,7 @@ proc mangleName(m: BModule, s: PSym): Rope =
if chr notin {'A'..'Z','a'..'z','_','$','0'..'9'}:
return false
result = s.loc.r
if result == nil:
if result == "":
if s.kind == skField and s.name.s.validJsName:
result = rope(s.name.s)
elif s.kind == skTemp:
@@ -292,6 +290,21 @@ proc makeJSString(s: string, escapeNonAscii = true): Rope =
else:
result = escapeJSString(s).rope
proc makeJsNimStrLit(s: string): Rope =
var x = newStringOfCap(4*s.len+1)
x.add "["
var i = 0
if i < s.len:
x.addInt int64(s[i])
inc i
while i < s.len:
x.add ","
x.addInt int64(s[i])
inc i
x.add "]"
result = rope(x)
include jstypes
proc gen(p: PProc, n: PNode, r: var TCompRes)
@@ -317,7 +330,9 @@ proc isSimpleExpr(p: PProc; n: PNode): bool =
# calls all the way down --> can stay expression based
case n.kind
of nkCallKinds, nkBracketExpr, nkDotExpr, nkPar, nkTupleConstr,
nkObjConstr, nkBracket, nkCurly:
nkObjConstr, nkBracket, nkCurly,
nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr,
nkConv, nkHiddenStdConv, nkHiddenSubConv:
for c in n:
if not p.isSimpleExpr(c): return false
result = true
@@ -464,7 +479,7 @@ proc maybeMakeTemp(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rope] =
b = a
if needsTemp(p, n):
# if we have tmp just use it
if x.tmpLoc != nil and (mapType(n.typ) == etyBaseIndex or n.kind in {nkHiddenDeref, nkDerefExpr}):
if x.tmpLoc != "" and (mapType(n.typ) == etyBaseIndex or n.kind in {nkHiddenDeref, nkDerefExpr}):
b = "$1[0][$1[1]]" % [x.tmpLoc]
(a: a, tmp: b)
else:
@@ -481,10 +496,10 @@ proc maybeMakeTempAssignable(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rop
b = a
if needsTemp(p, n):
# if we have tmp just use it
if x.tmpLoc != nil and (mapType(n.typ) == etyBaseIndex or n.kind in {nkHiddenDeref, nkDerefExpr}):
if x.tmpLoc != "" and (mapType(n.typ) == etyBaseIndex or n.kind in {nkHiddenDeref, nkDerefExpr}):
b = "$1[0][$1[1]]" % [x.tmpLoc]
result = (a: a, tmp: b)
elif x.tmpLoc != nil and n.kind == nkBracketExpr:
elif x.tmpLoc != "" and n.kind == nkBracketExpr:
# genArrayAddr
var
address, index: TCompRes
@@ -745,7 +760,7 @@ proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) =
else:
lineF(p, "$1;$n", [src.rdLoc])
src.kind = resNone
src.res = nil
src.res = ""
proc genTry(p: PProc, n: PNode, r: var TCompRes) =
# code to generate:
@@ -800,7 +815,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
moveInto(p, a, r)
if i > 1: lineF(p, "}$n", [])
else:
var orExpr: Rope = nil
var orExpr: Rope = ""
var excAlias: PNode = nil
useMagic(p, "isObj")
@@ -813,13 +828,13 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
excAlias = it[2]
# If this is a ``except exc as sym`` branch there must be no following
# nodes
doAssert orExpr == nil
doAssert orExpr == ""
elif it.kind == nkType:
throwObj = it
else:
internalError(p.config, n.info, "genTryStmt")
if orExpr != nil: orExpr.add("||")
if orExpr != "": orExpr.add("||")
# Generate the correct type checking code depending on whether this is a
# NIM-native or a JS-native exception
# if isJsObject(throwObj.typ):
@@ -907,7 +922,7 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
gen(p, e[1], b)
if j != itLen - 2:
lineF(p, "$1 >= $2 && $1 <= $3 || $n", [cond.rdLoc, a.rdLoc, b.rdLoc])
else:
else:
lineF(p, "$1 >= $2 && $1 <= $3", [cond.rdLoc, a.rdLoc, b.rdLoc])
else:
var v = copyNode(e[0])
@@ -998,7 +1013,7 @@ proc genBreakStmt(p: PProc, n: PNode) =
proc genAsmOrEmitStmt(p: PProc, n: PNode) =
genLineDir(p, n)
p.body.add p.indentLine(nil)
p.body.add p.indentLine("")
for i in 0..<n.len:
let it = n[i]
case it.kind
@@ -1016,12 +1031,12 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) =
if it.typ.kind == tyPointer:
# A fat pointer is disguised as an array
r.res = r.address
r.address = nil
r.address = ""
r.typ = etyNone
elif r.typ == etyBaseIndex:
# Deference first
r.res = "$1[$2]" % [r.address, r.res]
r.address = nil
r.address = ""
r.typ = etyNone
p.body.add(r.rdLoc)
@@ -1055,12 +1070,12 @@ proc genIf(p: PProc, n: PNode, r: var TCompRes) =
line(p, repeat('}', toClose) & "\L")
proc generateHeader(p: PProc, typ: PType): Rope =
result = nil
result = ""
for i in 1..<typ.n.len:
assert(typ.n[i].kind == nkSym)
var param = typ.n[i].sym
if isCompileTimeOnly(param.typ): continue
if result != nil: result.add(", ")
if result != "": result.add(", ")
var name = mangleName(p.module, param)
result.add(name)
if mapType(param.typ) == etyBaseIndex:
@@ -1135,7 +1150,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
elif b.typ == etyBaseIndex:
lineF(p, "$# = [$#, $#];$n", [a.res, b.address, b.res])
elif b.typ == etyNone:
internalAssert p.config, b.address == nil
internalAssert p.config, b.address == ""
lineF(p, "$# = [$#, 0];$n", [a.address, b.res])
elif x.typ.kind == tyVar and y.typ.kind == tyPtr:
lineF(p, "$# = [$#, $#];$n", [a.res, b.address, b.res])
@@ -1143,13 +1158,13 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
elif a.typ == etyBaseIndex:
# array indexing may not map to var type
if b.address != nil:
if b.address != "":
lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
else:
lineF(p, "$1 = $2;$n", [a.address, b.res])
else:
internalError(p.config, x.info, $("genAsgn", b.typ, a.typ))
elif b.address != nil:
elif b.address != "":
lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
else:
lineF(p, "$1 = $2;$n", [a.address, b.res])
@@ -1201,7 +1216,7 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
else:
if b[1].kind != nkSym: internalError(p.config, b[1].info, "genFieldAddr")
var f = b[1].sym
if f.loc.r == nil: f.loc.r = mangleName(p.module, f)
if f.loc.r == "": f.loc.r = mangleName(p.module, f)
r.res = makeJSString($f.loc.r)
internalAssert p.config, a.typ != etyBaseIndex
r.address = a.res
@@ -1229,7 +1244,7 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
else:
if n[1].kind != nkSym: internalError(p.config, n[1].info, "genFieldAccess")
var f = n[1].sym
if f.loc.r == nil: f.loc.r = mangleName(p.module, f)
if f.loc.r == "": f.loc.r = mangleName(p.module, f)
r.res = "$1.$2" % [r.res, f.loc.r]
mkTemp(1)
r.kind = resExpr
@@ -1250,11 +1265,11 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) =
# Field symbol
var field = accessExpr[1].sym
internalAssert p.config, field.kind == skField
if field.loc.r == nil: field.loc.r = mangleName(p.module, field)
if field.loc.r == "": field.loc.r = mangleName(p.module, field)
# Discriminant symbol
let disc = checkExpr[2].sym
internalAssert p.config, disc.kind == skField
if disc.loc.r == nil: disc.loc.r = mangleName(p.module, disc)
if disc.loc.r == "": disc.loc.r = mangleName(p.module, disc)
var setx: TCompRes
gen(p, checkExpr[1], setx)
@@ -1271,7 +1286,7 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) =
useMagic(p, "reprDiscriminant") # no need to offset by firstOrd unlike for cgen
let msg = genFieldDefect(p.config, field.name.s, disc)
lineF(p, "if ($1[$2.$3]$4undefined) { raiseFieldError2(makeNimstrLit($5), reprDiscriminant($2.$3, $6)); }$n",
setx.res, tmp, disc.loc.r, if negCheck: ~"!==" else: ~"===",
setx.res, tmp, disc.loc.r, if negCheck: "!==" else: "===",
makeJSString(msg), genTypeInfo(p, disc.typ))
if addrTyp != nil and mapType(p, addrTyp) == etyBaseIndex:
@@ -1320,7 +1335,7 @@ proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) =
genFieldAddr(p, n, r)
else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
r.typ = mapType(n.typ)
if r.res == nil: internalError(p.config, n.info, "genArrayAccess")
if r.res == "": internalError(p.config, n.info, "genArrayAccess")
if ty.kind == tyCstring:
r.res = "$1.charCodeAt($2)" % [r.address, r.res]
elif r.typ == etyBaseIndex:
@@ -1347,11 +1362,11 @@ template isIndirect(x: PSym): bool =
proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) =
let s = n.sym
if s.loc.r == nil: internalError(p.config, n.info, "genAddr: 3")
if s.loc.r == "": internalError(p.config, n.info, "genAddr: 3")
case s.kind
of skParam:
r.res = s.loc.r
r.address = nil
r.address = ""
r.typ = etyNone
of skVar, skLet, skResult:
r.kind = resExpr
@@ -1367,7 +1382,7 @@ proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) =
r.res = s.loc.r & "[0]"
else:
r.res = s.loc.r
r.address = nil
r.address = ""
elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex:
# for ease of code generation, we do not distinguish between
# sfAddrTaken and sfGlobal.
@@ -1466,10 +1481,10 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
var s = n.sym
case s.kind
of skVar, skLet, skParam, skTemp, skResult, skForVar:
if s.loc.r == nil:
if s.loc.r == "":
internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
if sfCompileTime in s.flags:
genVarInit(p, s, if s.ast != nil: s.ast else: newNodeI(nkEmpty, s.info))
genVarInit(p, s, if s.astdef != nil: s.astdef else: newNodeI(nkEmpty, s.info))
if s.kind == skParam:
genCopyForParamIfNeeded(p, n)
let k = mapType(p, s.typ)
@@ -1491,7 +1506,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
r.res = s.loc.r
of skConst:
genConstant(p, s)
if s.loc.r == nil:
if s.loc.r == "":
internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
r.res = s.loc.r
of skProc, skFunc, skConverter, skMethod:
@@ -1511,7 +1526,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
else:
genProcForSymIfNeeded(p, s)
else:
if s.loc.r == nil:
if s.loc.r == "":
internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
if mapType(p, s.typ) == etyBaseIndex:
r.address = s.loc.r
@@ -1536,7 +1551,7 @@ proc genDeref(p: PProc, n: PNode, r: var TCompRes) =
r.res = "$1[1]" % [tmp]
r.tmpLoc = tmp
elif a.typ == etyBaseIndex:
if a.tmpLoc != nil:
if a.tmpLoc != "":
r.tmpLoc = a.tmpLoc
r.res = a.rdLoc
else:
@@ -1659,9 +1674,9 @@ proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType;
proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
# don't call '$' here for efficiency:
let f = n[0].sym
if f.loc.r == nil: f.loc.r = mangleName(p.module, f)
if f.loc.r == "": f.loc.r = mangleName(p.module, f)
if sfInfixCall in f.flags:
let pat = n[0].sym.loc.r.data
let pat = $n[0].sym.loc.r
internalAssert p.config, pat.len > 0
if pat.contains({'#', '(', '@'}):
var typ = skipTypes(n[0].typ, abstractInst)
@@ -1671,10 +1686,10 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
if n.len != 1:
gen(p, n[1], r)
if r.typ == etyBaseIndex:
if r.address == nil:
if r.address == "":
globalError(p.config, n.info, "cannot invoke with infix syntax")
r.res = "$1[$2]" % [r.address, r.res]
r.address = nil
r.address = ""
r.typ = etyNone
r.res.add(".")
var op: TCompRes
@@ -1742,16 +1757,22 @@ proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: v
t = t[0]
proc arrayTypeForElemType(typ: PType): string =
# XXX This should also support tyEnum and tyBool
let typ = typ.skipTypes(abstractRange)
case typ.kind
of tyInt, tyInt32: "Int32Array"
of tyInt16: "Int16Array"
of tyInt8: "Int8Array"
of tyUInt, tyUInt32: "Uint32Array"
of tyUInt16: "Uint16Array"
of tyUInt8: "Uint8Array"
of tyUInt8, tyChar, tyBool: "Uint8Array"
of tyFloat32: "Float32Array"
of tyFloat64, tyFloat: "Float64Array"
of tyEnum:
case typ.size
of 1: "Uint8Array"
of 2: "Uint16Array"
of 4: "Uint32Array"
else: ""
else: ""
proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
@@ -1820,12 +1841,12 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
result = createVar(p, lastSon t, indirect)
else:
internalError(p.config, "createVar: " & $t.kind)
result = nil
result = ""
else:
internalError(p.config, "createVar: " & $t.kind)
result = nil
result = ""
template returnType: untyped = ~""
template returnType: untyped = ""
proc genVarInit(p: PProc, v: PSym, n: PNode) =
var
@@ -1923,10 +1944,9 @@ proc genVarStmt(p: PProc, n: PNode) =
proc genConstant(p: PProc, c: PSym) =
if lfNoDecl notin c.loc.flags and not p.g.generatedSyms.containsOrIncl(c.id):
let oldBody = p.body
p.body = nil
#genLineDir(p, c.ast)
genVarInit(p, c, c.ast)
let oldBody = move p.body
#genLineDir(p, c.astdef)
genVarInit(p, c, c.astdef)
p.g.constants.add(p.body)
p.body = oldBody
@@ -1978,7 +1998,7 @@ proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) =
else:
r.res.add("$1 || [])" % [a.res])
proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = nil) =
proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = "") =
useMagic(p, magic)
r.res.add(magic & "(")
var a: TCompRes
@@ -1987,7 +2007,7 @@ proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope =
if magic == "reprAny":
# the pointer argument in reprAny is expandend to
# (pointedto, pointer), so we need to fill it
if a.address.isNil:
if a.address.len == 0:
r.res.add(a.res)
r.res.add(", null")
else:
@@ -1995,14 +2015,14 @@ proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope =
else:
r.res.add(a.res)
if not typ.isNil:
if typ != "":
r.res.add(", ")
r.res.add(typ)
r.res.add(")")
proc genRepr(p: PProc, n: PNode, r: var TCompRes) =
let t = skipTypes(n[1].typ, abstractVarRange)
case t.kind:
case t.kind
of tyInt..tyInt64, tyUInt..tyUInt64:
genReprAux(p, n, r, "reprInt")
of tyChar:
@@ -2321,7 +2341,7 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
let val = it[1]
gen(p, val, a)
var f = it[0].sym
if f.loc.r == nil: f.loc.r = mangleName(p.module, f)
if f.loc.r == "": f.loc.r = mangleName(p.module, f)
fieldIDs.incl(lookupFieldAgain(nTyp, f).id)
let typ = val.typ.skipTypes(abstractInst)
@@ -2382,7 +2402,7 @@ proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) =
gen(p, n[0][0], r)
else:
gen(p, n[0], r)
if r.res == nil: internalError(p.config, n.info, "convStrToCStr")
if r.res == "": internalError(p.config, n.info, "convStrToCStr")
useMagic(p, "toJSStr")
r.res = "toJSStr($1)" % [r.res]
r.kind = resExpr
@@ -2394,7 +2414,7 @@ proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) =
gen(p, n[0][0], r)
else:
gen(p, n[0], r)
if r.res == nil: internalError(p.config, n.info, "convCStrToStr")
if r.res == "": internalError(p.config, n.info, "convCStrToStr")
useMagic(p, "cstrToNimstr")
r.res = "cstrToNimstr($1)" % [r.res]
r.kind = resExpr
@@ -2424,11 +2444,11 @@ proc genProcBody(p: PProc, prc: PSym): Rope =
makeJSString(prc.owner.name.s & '.' & prc.name.s),
makeJSString(toFilenameOption(p.config, prc.info.fileIndex, foStacktrace)))
else:
result = nil
result = ""
if p.beforeRetNeeded:
result.add p.indentLine(~"BeforeRet: {$n")
result.add p.indentLine("BeforeRet: {\n")
result.add p.body
result.add p.indentLine(~"};$n")
result.add p.indentLine("};\n")
else:
result.add(p.body)
if prc.typ.callConv == ccSysCall:
@@ -2438,8 +2458,8 @@ proc genProcBody(p: PProc, prc: PSym): Rope =
result.add(frameDestroy(p))
proc optionalLine(p: Rope): Rope =
if p == nil:
return nil
if p == "":
return ""
else:
return p & "\L"
@@ -2452,8 +2472,8 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
# echo "BEGIN generating code for: " & prc.name.s
var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options)
p.up = oldProc
var returnStmt: Rope = nil
var resultAsgn: Rope = nil
var returnStmt: Rope = ""
var resultAsgn: Rope = ""
var name = mangleName(p.module, prc)
let header = generateHeader(p, prc.typ)
if prc.typ[0] != nil and sfPure notin prc.flags:
@@ -2497,7 +2517,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
optionalLine(p.indentLine(returnStmt))])
else:
# if optLineDir in p.config.options:
# result.add(~"\L")
# result.add("\L")
if p.config.hcrOn:
# Here, we introduce thunks that create the equivalent of a jump table
@@ -2520,7 +2540,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
dec p.extraIndent
result.add p.indentLine(def)
result.add p.indentLine(~"}$n")
result.add p.indentLine("}\n")
#if gVerbosity >= 3:
# echo "END generated code for: " & prc.name.s
@@ -2528,7 +2548,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
proc genStmt(p: PProc, n: PNode) =
var r: TCompRes
gen(p, n, r)
if r.res != nil: lineF(p, "$#;$n", [r.res])
if r.res != "": lineF(p, "$#;$n", [r.res])
proc genPragma(p: PProc, n: PNode) =
for it in n.sons:
@@ -2568,7 +2588,7 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) =
r.res = "($1 - ($2 $3))" % [rope minuend, r.res, trimmer]
elif (src.kind == tyPtr and mapType(p, src) == etyObject) and dest.kind == tyPointer:
r.address = r.res
r.res = ~"null"
r.res = "null"
r.typ = etyBaseIndex
elif (dest.kind == tyPtr and mapType(p, dest) == etyObject) and src.kind == tyPointer:
r.res = r.address
@@ -2577,8 +2597,8 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) =
proc gen(p: PProc, n: PNode, r: var TCompRes) =
r.typ = etyNone
if r.kind != resCallee: r.kind = resNone
#r.address = nil
r.res = nil
#r.address = ""
r.res = ""
case n.kind
of nkSym:
@@ -2602,11 +2622,11 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
r.kind = resExpr
of nkStrLit..nkTripleStrLit:
if skipTypes(n.typ, abstractVarRange).kind == tyString:
if n.strVal.len != 0:
if n.strVal.len <= 64:
r.res = makeJsNimStrLit(n.strVal)
else:
useMagic(p, "makeNimstrLit")
r.res = "makeNimstrLit($1)" % [makeJSString(n.strVal)]
else:
r.res = rope"[]"
else:
r.res = makeJSString(n.strVal, false)
r.kind = resExpr
@@ -2717,7 +2737,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
var s = n[namePos].sym
if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}:
genSym(p, n[namePos], r)
r.res = nil
r.res = ""
of nkGotoState, nkState:
globalError(p.config, n.info, "First class iterators not implemented")
of nkPragmaBlock: gen(p, n.lastSon, r)
@@ -2834,7 +2854,7 @@ proc getClassName(t: PType): Rope =
s = skipTypes(t, abstractPtrs).sym
if s.isNil or sfAnon in s.flags:
doAssert(false, "cannot retrieve class name")
if s.loc.r != nil: result = s.loc.r
if s.loc.r != "": result = s.loc.r
else: result = rope(s.name.s)
proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =

View File

@@ -19,13 +19,13 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
s, u: Rope
field: PSym
b: PNode
result = nil
result = ""
case n.kind
of nkRecList:
if n.len == 1:
result = genObjectFields(p, typ, n[0])
else:
s = nil
s = ""
for i in 0..<n.len:
if i > 0: s.add(", \L")
s.add(genObjectFields(p, typ, n[i]))
@@ -44,13 +44,13 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
s = genTypeInfo(p, field.typ)
for i in 1..<n.len:
b = n[i] # branch
u = nil
u = ""
case b.kind
of nkOfBranch:
if b.len < 2:
internalError(p.config, b.info, "genObjectFields; nkOfBranch broken")
for j in 0..<b.len - 1:
if u != nil: u.add(", ")
if u != "": u.add(", ")
if b[j].kind == nkRange:
u.addf("[$1, $2]", [rope(getOrdValue(b[j][0])),
rope(getOrdValue(b[j][1]))])
@@ -59,7 +59,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
of nkElse:
u = rope(lengthOrd(p.config, field.typ))
else: internalError(p.config, n.info, "genObjectFields(nkRecCase)")
if result != nil: result.add(", \L")
if result != "": result.add(", \L")
result.addf("[setConstr($1), $2]",
[u, genObjectFields(p, typ, lastSon(b))])
result = ("{kind: 3, offset: \"$1\", len: $3, " &
@@ -84,7 +84,7 @@ proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
[name, genTypeInfo(p, typ[0].skipTypes(skipPtrs))])
proc genTupleFields(p: PProc, typ: PType): Rope =
var s: Rope = nil
var s: Rope = ""
for i in 0..<typ.len:
if i > 0: s.add(", \L")
s.addf("{kind: 1, offset: \"Field$1\", len: 0, " &
@@ -102,7 +102,7 @@ proc genTupleInfo(p: PProc, typ: PType, name: Rope) =
p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)])
proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
var s: Rope = nil
var s: Rope = ""
for i in 0..<typ.n.len:
if (typ.n[i].kind != nkSym): internalError(p.config, typ.n.info, "genEnumInfo")
let field = typ.n[i].sym

View File

@@ -245,6 +245,13 @@ proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen
if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions:
owner.flags.incl sfInjectDestructors
proc genCreateEnv(env: PNode): PNode =
var c = newNodeIT(nkObjConstr, env.info, env.typ)
c.add newNodeIT(nkType, env.info, env.typ)
let e = copyTree(env)
e.flags.incl nfFirstWrite2
result = newAsgnStmt(e, c)
proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
if liftingHarmful(g.config, owner): return n
@@ -268,7 +275,8 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
addVar(v, env)
result.add(v)
# add 'new' statement:
result.add newCall(getSysSym(g, n.info, "internalNew"), env)
#result.add newCall(getSysSym(g, n.info, "internalNew"), env)
result.add genCreateEnv(env)
createTypeBoundOpsLL(g, env.typ, n.info, idgen, owner)
result.add makeClosure(g, idgen, iter, env, n.info)
@@ -596,7 +604,7 @@ proc rawClosureCreation(owner: PSym;
addVar(v, unowned)
# add 'new' statement:
result.add(newCall(getSysSym(d.graph, env.info, "internalNew"), env))
result.add genCreateEnv(env)
if optOwnedRefs in d.graph.config.globalOptions:
let unowned = c.unownedEnvVars[owner.id]
assert unowned != nil
@@ -658,7 +666,7 @@ proc closureCreationForIter(iter: PNode;
var vs = newNodeI(nkVarSection, iter.info)
addVar(vs, vnode)
result.add(vs)
result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode))
result.add genCreateEnv(vnode)
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, owner)
let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
@@ -943,7 +951,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
addVar(v, newSymNode(env))
result.add(v)
# add 'new' statement:
result.add(newCall(getSysSym(g, env.info, "internalNew"), env.newSymNode))
result.add genCreateEnv(env.newSymNode)
createTypeBoundOpsLL(g, env.typ, body.info, idgen, owner)
elif op.kind == nkStmtListExpr:

View File

@@ -505,11 +505,11 @@ proc getNumber(L: var Lexer, result: var Token) =
of tkUInt16Lit: setNumber result.iNumber, xi and 0xffff
of tkUInt32Lit: setNumber result.iNumber, xi and 0xffffffff
of tkFloat32Lit:
setNumber result.fNumber, (cast[PFloat32](addr(xi)))[]
setNumber result.fNumber, (cast[ptr float32](addr(xi)))[]
# note: this code is endian neutral!
# XXX: Test this on big endian machine!
of tkFloat64Lit, tkFloatLit:
setNumber result.fNumber, (cast[PFloat64](addr(xi)))[]
setNumber result.fNumber, (cast[ptr float64](addr(xi)))[]
else: internalError(L.config, getLineInfo(L), "getNumber")
# Bounds checks. Non decimal literals are allowed to overflow the range of
@@ -907,7 +907,7 @@ proc getSymbol(L: var Lexer, tok: var Token) =
inc(pos)
suspicious = true
of '\x80'..'\xFF':
if c in UnicodeOperatorStartChars and unicodeOperators in L.config.features and unicodeOprLen(L.buf, pos)[0] != 0:
if c in UnicodeOperatorStartChars and unicodeOprLen(L.buf, pos)[0] != 0:
break
else:
h = h !& ord(c)
@@ -943,7 +943,7 @@ proc getOperator(L: var Lexer, tok: var Token) =
if c in OpChars:
h = h !& ord(c)
inc(pos)
elif c in UnicodeOperatorStartChars and unicodeOperators in L.config.features:
elif c in UnicodeOperatorStartChars:
let oprLen = unicodeOprLen(L.buf, pos)[0]
if oprLen == 0: break
for i in 0..<oprLen:
@@ -1244,7 +1244,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
else:
case c
of UnicodeOperatorStartChars:
if unicodeOperators in L.config.features and unicodeOprLen(L.buf, L.bufpos)[0] != 0:
if unicodeOprLen(L.buf, L.bufpos)[0] != 0:
getOperator(L, tok)
else:
getSymbol(L, tok)
@@ -1355,7 +1355,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
getNumber(L, tok)
let c = L.buf[L.bufpos]
if c in SymChars+{'_'}:
if c in UnicodeOperatorStartChars and unicodeOperators in L.config.features and
if c in UnicodeOperatorStartChars and
unicodeOprLen(L.buf, L.bufpos)[0] != 0:
discard
else:
@@ -1370,7 +1370,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
getNumber(L, tok)
let c = L.buf[L.bufpos]
if c in SymChars+{'_'}:
if c in UnicodeOperatorStartChars and unicodeOperators in L.config.features and
if c in UnicodeOperatorStartChars and
unicodeOprLen(L.buf, L.bufpos)[0] != 0:
discard
else:

View File

@@ -96,6 +96,7 @@ type
hintPattern = "Pattern", hintExecuting = "Exec", hintLinking = "Link", hintDependency = "Dependency",
hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace",
hintGCStats = "GCStats", hintGlobalVar = "GlobalVar", hintExpandMacro = "ExpandMacro",
hintAmbiguousEnum = "AmbiguousEnum",
hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
hintMsgOrigin = "MsgOrigin", # since 1.3.5
hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
@@ -209,6 +210,7 @@ const
hintGCStats: "$1",
hintGlobalVar: "global variable declared here",
hintExpandMacro: "expanded macro: $1",
hintAmbiguousEnum: "$1",
hintUser: "$1",
hintUserRaw: "$1",
hintExtendedContext: "$1",

View File

@@ -10,7 +10,9 @@
import
std/[strutils, os, tables, terminal, macros, times],
std/private/miscdollars,
options, ropes, lineinfos, pathutils, strutils2
options, lineinfos, pathutils, strutils2
import ropes except `%`
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions]
@@ -43,18 +45,16 @@ proc toCChar*(c: char; result: var string) {.inline.} =
result.add c
proc makeCString*(s: string): Rope =
result = nil
var res = newStringOfCap(int(s.len.toFloat * 1.1) + 1)
res.add("\"")
result = newStringOfCap(int(s.len.toFloat * 1.1) + 1)
result.add("\"")
for i in 0..<s.len:
# line wrapping of string litterals in cgen'd code was a bad idea, e.g. causes: bug #16265
# It also makes reading c sources or grepping harder, for zero benefit.
# const MaxLineLength = 64
# if (i + 1) mod MaxLineLength == 0:
# res.add("\"\L\"")
toCChar(s[i], res)
res.add('\"')
result.add(rope(res))
toCChar(s[i], result)
result.add('\"')
proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile): TFileInfo =
result.fullPath = fullPath

View File

@@ -92,9 +92,15 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
return
self.processCmdLineAndProjectPath(conf)
var graph = newModuleGraph(cache, conf)
if not self.loadConfigsAndProcessCmdLine(cache, conf, graph):
return
if conf.selectedGC == gcUnselected:
if conf.backend in {backendC, backendCpp, backendObjc}:
initOrcDefines(conf)
mainCommand(graph)
if conf.hasHint(hintGCStats): echo(GC_getStatistics())
#echo(GC_getStatistics())

View File

@@ -213,9 +213,9 @@ type
strictFuncs,
views,
strictNotNil,
overloadableEnums, # not experimental anymore
overloadableEnums, # deadcode
strictEffects,
unicodeOperators,
unicodeOperators, # deadcode
flexibleOptionalParams
LegacyFeature* = enum
@@ -504,7 +504,7 @@ when defined(nimDebugUtils):
export debugutils
proc initConfigRefCommon(conf: ConfigRef) =
conf.selectedGC = gcRefc
conf.selectedGC = gcUnselected
conf.verbosity = 1
conf.hintProcessingDots = true
conf.options = DefaultOptions

View File

@@ -183,7 +183,7 @@ type
arLentValue, # lent value
arStrange # it is a strange beast like 'typedesc[var T]'
proc exprRoot*(n: PNode): PSym =
proc exprRoot*(n: PNode; allowCalls = true): PSym =
var it = n
while true:
case it.kind
@@ -204,7 +204,7 @@ proc exprRoot*(n: PNode): PSym =
if it.len > 0 and it.typ != nil: it = it.lastSon
else: break
of nkCallKinds:
if it.typ != nil and it.typ.kind in {tyVar, tyLent} and it.len > 1:
if allowCalls and it.typ != nil and it.typ.kind in {tyVar, tyLent} and it.len > 1:
# See RFC #7373, calls returning 'var T' are assumed to
# return a view into the first argument (if there is one):
it = it[1]
@@ -224,7 +224,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
const kinds = {skVar, skResult, skTemp, skParam, skLet, skForVar}
if n.sym.kind == skParam:
result = if n.sym.typ.kind in {tyVar, tySink}: arLValue else: arAddressableConst
elif n.sym.kind == skConst and dontInlineConstant(n, n.sym.ast):
elif n.sym.kind == skConst and dontInlineConstant(n, n.sym.astdef):
result = arAddressableConst
elif n.sym.kind in kinds:
if n.sym.kind in {skParam, skLet, skForVar}:

View File

@@ -146,7 +146,7 @@ proc matches(c: PPatternContext, p, n: PNode): bool =
elif n.kind == nkSym and n.sym.kind == skConst:
# try both:
if p.kind == nkSym: result = p.sym == n.sym
elif matches(c, p, n.sym.ast): result = true
elif matches(c, p, n.sym.astdef): result = true
elif p.kind == nkPattern:
# pattern operators: | *
let opr = p[0].ident.s

View File

@@ -313,7 +313,7 @@ proc expectDynlibNode(c: PContext, n: PNode): PNode =
# {.dynlib: myGetProcAddr(...).}
result = c.semExpr(c, n[1])
if result.kind == nkSym and result.sym.kind == skConst:
result = result.sym.ast # look it up
result = result.sym.astdef # look it up
if result.typ == nil or result.typ.kind notin {tyPointer, tyString, tyProc}:
localError(c.config, n.info, errStringLiteralExpected)
result = newEmptyStrNode(c, n)
@@ -960,7 +960,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
incl(sym.loc.flags, lfHeader)
incl(sym.loc.flags, lfNoDecl)
# implies nodecl, because otherwise header would not make sense
if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
if sym.loc.r == "": sym.loc.r = rope(sym.name.s)
of wNoSideEffect:
noVal(c, it)
if sym != nil:
@@ -998,7 +998,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
of wExplain:
sym.flags.incl sfExplain
of wDeprecated:
if sym != nil and sym.kind in routineKinds + {skType, skVar, skLet}:
if sym != nil and sym.kind in routineKinds + {skType, skVar, skLet, skConst}:
if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it)
incl(sym.flags, sfDeprecated)
elif sym != nil and sym.kind != skModule:
@@ -1247,7 +1247,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
elif comesFromPush and whichKeyword(ident) != wInvalid:
discard "ignore the .push pragma; it doesn't apply"
else:
if sym == nil or (sym.kind in {skVar, skLet, skParam, skIterator,
if sym == nil or (sym.kind in {skVar, skLet, skConst, skParam, skIterator,
skField, skProc, skFunc, skConverter, skMethod, skType}):
n[i] = semCustomPragma(c, it)
elif sym != nil:
@@ -1290,7 +1290,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
sfImportc in sym.flags and lib != nil:
incl(sym.loc.flags, lfDynamicLib)
addToLib(lib, sym)
if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
if sym.loc.r == "": sym.loc.r = rope(sym.name.s)
proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
if n == nil: return false

View File

@@ -398,18 +398,18 @@ proc atom(g: TSrcGen; n: PNode): string =
of nkUInt64Lit: result = ulitAux(g, n, n.intVal, 8) & "\'u64"
of nkFloatLit:
if n.flags * {nfBase2, nfBase8, nfBase16} == {}: result = $(n.floatVal)
else: result = litAux(g, n, (cast[PInt64](addr(n.floatVal)))[] , 8)
else: result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[] , 8)
of nkFloat32Lit:
if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
result = $n.floatVal & "\'f32"
else:
f = n.floatVal.float32
result = litAux(g, n, (cast[PInt32](addr(f)))[], 4) & "\'f32"
result = litAux(g, n, (cast[ptr int32](addr(f)))[], 4) & "\'f32"
of nkFloat64Lit:
if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
result = $n.floatVal & "\'f64"
else:
result = litAux(g, n, (cast[PInt64](addr(n.floatVal)))[], 8) & "\'f64"
result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f64"
of nkNilLit: result = "nil"
of nkType:
if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s

View File

@@ -7,217 +7,57 @@
# distribution, for details about the copyright.
#
# Ropes for the C code generator
#
# Ropes are a data structure that represents a very long string
# efficiently; especially concatenation is done in O(1) instead of O(N).
# Ropes make use a lazy evaluation: They are essentially concatenation
# trees that are only flattened when converting to a native Nim
# string or when written to disk. The empty string is represented by a
# nil pointer.
# A little picture makes everything clear:
#
# "this string" & " is internally " & "represented as"
#
# con -- inner nodes do not contain raw data
# / \
# / \
# / \
# con "represented as"
# / \
# / \
# / \
# / \
# / \
#"this string" " is internally "
#
# Note that this is the same as:
# "this string" & (" is internally " & "represented as")
#
# con
# / \
# / \
# / \
# "this string" con
# / \
# / \
# / \
# / \
# / \
#" is internally " "represented as"
#
# The 'con' operator is associative! This does not matter however for
# the algorithms we use for ropes.
#
# Note that the left and right pointers are not needed for leaves.
# Leaves have relatively high memory overhead (~30 bytes on a 32
# bit machines) and we produce many of them. This is why we cache and
# share leaves across different rope trees.
# To cache them they are inserted in a `cache` array.
# Ropes for the C code generator. Ropes are mapped to `string` directly nowadays.
import
hashes
import hashes
from pathutils import AbsoluteFile
when defined(nimPreviewSlimSystem):
import std/[assertions, syncio, formatfloat]
type
FormatStr* = string # later we may change it to CString for better
# performance of the code generator (assignments
# copy the format strings
# though it is not necessary)
Rope* = ref RopeObj
RopeObj*{.acyclic.} = object of RootObj # the empty rope is represented
# by nil to safe space
left, right: Rope
L: int # <= 0 if a leaf
data*: string
Rope* = string
proc len*(a: Rope): int =
## the rope's length
if a == nil: result = 0
else: result = abs a.L
proc newRopeAppender*(): string {.inline.} =
result = newString(0)
proc newRope(data: string = ""): Rope =
new(result)
result.L = -data.len
result.data = data
proc freeze*(r: Rope) {.inline.} = discard
when compileOption("tlsEmulation"): # fixme: be careful if you want to make ropes support multiple threads
var
cache: array[0..2048*2 - 1, Rope]
else:
var
cache {.threadvar.} : array[0..2048*2 - 1, Rope]
proc resetRopeCache* = discard
proc resetRopeCache* =
for i in low(cache)..high(cache):
cache[i] = nil
proc ropeInvariant(r: Rope): bool =
if r == nil:
result = true
else:
result = true #
# if r.data <> snil then
# result := true
# else begin
# result := (r.left <> nil) and (r.right <> nil);
# if result then result := ropeInvariant(r.left);
# if result then result := ropeInvariant(r.right);
# end
var gCacheTries* = 0
var gCacheMisses* = 0
var gCacheIntTries* = 0
proc insertInCache(s: string): Rope =
inc gCacheTries
var h = hash(s) and high(cache)
result = cache[h]
if isNil(result) or result.data != s:
inc gCacheMisses
result = newRope(s)
cache[h] = result
proc rope*(s: string): Rope =
## Converts a string to a rope.
if s.len == 0:
result = nil
else:
result = insertInCache(s)
assert(ropeInvariant(result))
template rope*(s: string): string = s
proc rope*(i: BiggestInt): Rope =
## Converts an int to a rope.
inc gCacheIntTries
result = rope($i)
proc rope*(f: BiggestFloat): Rope =
## Converts a float to a rope.
result = rope($f)
proc `&`*(a, b: Rope): Rope =
if a == nil:
result = b
elif b == nil:
result = a
else:
result = newRope()
result.L = abs(a.L) + abs(b.L)
result.left = a
result.right = b
proc `&`*(a: Rope, b: string): Rope =
## the concatenation operator for ropes.
result = a & rope(b)
proc `&`*(a: string, b: Rope): Rope =
## the concatenation operator for ropes.
result = rope(a) & b
proc `&`*(a: openArray[Rope]): Rope =
## the concatenation operator for an openarray of ropes.
for i in 0..high(a): result = result & a[i]
proc add*(a: var Rope, b: Rope) =
## adds `b` to the rope `a`.
a = a & b
proc add*(a: var Rope, b: string) =
## adds `b` to the rope `a`.
a = a & b
iterator leaves*(r: Rope): string =
## iterates over any leaf string in the rope `r`.
if r != nil:
var stack = @[r]
while stack.len > 0:
var it = stack.pop
while it.left != nil:
assert it.right != nil
stack.add(it.right)
it = it.left
assert(it != nil)
yield it.data
iterator items*(r: Rope): char =
## iterates over any character in the rope `r`.
for s in leaves(r):
for c in items(s): yield c
proc writeRope*(f: File, r: Rope) =
## writes a rope to a file.
for s in leaves(r): write(f, s)
write(f, r)
proc writeRope*(head: Rope, filename: AbsoluteFile): bool =
var f: File
if open(f, filename.string, fmWrite):
if head != nil: writeRope(f, head)
writeRope(f, head)
close(f)
result = true
else:
result = false
proc `$`*(r: Rope): string =
## converts a rope back to a string.
result = newString(r.len)
setLen(result, 0)
for s in leaves(r): result.add(s)
proc ropeConcat*(a: varargs[Rope]): Rope =
# not overloaded version of concat to speed-up `rfmt` a little bit
for i in 0..high(a): result = result & a[i]
proc prepend*(a: var Rope, b: Rope) = a = b & a
proc prepend*(a: var Rope, b: string) = a = b & a
proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope =
var i = 0
result = nil
result = newRopeAppender()
var num = 0
while i < frmt.len:
if frmt[i] == '$':
@@ -270,7 +110,6 @@ proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope =
else: break
if i - 1 >= start:
result.add(substr(frmt, start, i - 1))
assert(ropeInvariant(result))
proc `%`*(frmt: static[FormatStr], args: openArray[Rope]): Rope =
runtimeFormat(frmt, args)
@@ -279,21 +118,10 @@ template addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) =
## shortcut for ``add(c, frmt % args)``.
c.add(frmt % args)
when true:
template `~`*(r: string): Rope = r % []
else:
{.push stack_trace: off, line_trace: off.}
proc `~`*(r: static[string]): Rope =
# this is the new optimized "to rope" operator
# the mnemonic is that `~` looks a bit like a rope :)
var r {.global.} = r % []
return r
{.pop.}
const
bufSize = 1024 # 1 KB is reasonable
proc equalsFile*(r: Rope, f: File): bool =
proc equalsFile*(s: Rope, f: File): bool =
## returns true if the contents of the file `f` equal `r`.
var
buf: array[bufSize, char]
@@ -302,7 +130,7 @@ proc equalsFile*(r: Rope, f: File): bool =
btotal = 0
rtotal = 0
for s in leaves(r):
when true:
var spos = 0
rtotal += s.len
while spos < s.len:

View File

@@ -544,8 +544,8 @@ proc semResolvedCall(c: PContext, x: TCandidate,
for s in instantiateGenericParamList(c, gp, x.bindings):
case s.kind
of skConst:
if not s.ast.isNil:
x.call.add s.ast
if not s.astdef.isNil:
x.call.add s.astdef
else:
x.call.add c.graph.emptyNode
of skType:

View File

@@ -87,6 +87,14 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType
result = semExprCheck(c, n, flags, expectedType)
if result.typ == nil and efInTypeof in flags:
result.typ = c.voidType
elif (result.typ == nil or result.typ.kind == tyNone) and
result.kind == nkClosedSymChoice and
result[0].sym.kind == skEnumField:
# if overloaded enum field could not choose a type from a closed list,
# choose the first resolved enum field, i.e. the latest in scope
# to mirror old behavior
msgSymChoiceUseQualifier(c, result, hintAmbiguousEnum)
result = result[0]
elif result.typ == nil or result.typ == c.enforceVoidContext:
localError(c.config, n.info, errExprXHasNoType %
renderTree(result, {renderNoComments}))
@@ -108,7 +116,7 @@ proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
result = symChoice(c, n, s, scClosed)
proc inlineConst(c: PContext, n: PNode, s: PSym): PNode {.inline.} =
result = copyTree(s.ast)
result = copyTree(s.astdef)
if result.isNil:
localError(c.config, n.info, "constant of type '" & typeToString(s.typ) & "' has no value")
result = newSymNode(s)
@@ -241,7 +249,10 @@ proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool =
(skipTypes(dst, abstractInst).kind in IntegralTypes) or
(skipTypes(src, abstractInst-{tyTypeDesc}).kind in IntegralTypes)
if result and (dstSize > srcSize):
message(conf, info, warnCastSizes, "target type is larger than source type")
var warnMsg = "target type is larger than source type"
warnMsg.add("\n target type: '$1' ($2)" % [$dst, if dstSize == 1: "1 byte" else: $dstSize & " bytes"])
warnMsg.add("\n source type: '$1' ($2)" % [$src, if srcSize == 1: "1 byte" else: $srcSize & " bytes"])
message(conf, info, warnCastSizes, warnMsg)
if result and src.kind == tyNil:
return dst.size <= conf.target.ptrSize
@@ -363,10 +374,7 @@ proc semCast(c: PContext, n: PNode): PNode =
if tfHasMeta in targetType.flags:
localError(c.config, n[0].info, "cannot cast to a non concrete type: '$1'" % $targetType)
if not isCastable(c, targetType, castedExpr.typ, n.info):
let tar = $targetType
let alt = typeToString(targetType, preferDesc)
let msg = if tar != alt: tar & "=" & alt else: tar
localError(c.config, n.info, "expression cannot be cast to " & msg)
localError(c.config, n.info, "expression cannot be cast to '$1'" % $targetType)
result = newNodeI(nkCast, n.info)
result.typ = targetType
result.add copyTree(n[0])
@@ -1230,7 +1238,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
# It is clear that ``[]`` means two totally different things. Thus, we
# copy `x`'s AST into each context, so that the type fixup phase can
# deal with two different ``[]``.
if s.ast.safeLen == 0: result = inlineConst(c, n, s)
if s.astdef.safeLen == 0: result = inlineConst(c, n, s)
else: result = newSymNode(s, n.info)
of tyStatic:
if typ.n != nil:
@@ -2259,7 +2267,7 @@ proc instantiateCreateFlowVarCall(c: PContext; t: PType;
# codegen would fail:
if sfCompilerProc in result.flags:
result.flags.excl {sfCompilerProc, sfExportc, sfImportc}
result.loc.r = nil
result.loc.r = ""
proc setMs(n: PNode, s: PSym): PNode =
result = n
@@ -2836,7 +2844,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
defer:
if isCompilerDebug():
echo ("<", c.config$n.info, n, ?.result.typ)
template directLiteral(typeKind: TTypeKind) =
if result.typ == nil:
if expectedType != nil and (

View File

@@ -517,12 +517,12 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
"{.intdefine.} const was set to an invalid integer: '" &
g.config.symbols[s.name.s] & "'")
else:
result = copyTree(s.ast)
result = copyTree(s.astdef)
of mStrDefine:
if isDefined(g.config, s.name.s):
result = newStrNodeT(g.config.symbols[s.name.s], n, g)
else:
result = copyTree(s.ast)
result = copyTree(s.astdef)
of mBoolDefine:
if isDefined(g.config, s.name.s):
try:
@@ -532,9 +532,9 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
"{.booldefine.} const was set to an invalid bool: '" &
g.config.symbols[s.name.s] & "'")
else:
result = copyTree(s.ast)
result = copyTree(s.astdef)
else:
result = copyTree(s.ast)
result = copyTree(s.astdef)
of skProc, skFunc, skMethod:
result = n
of skParam:

View File

@@ -449,6 +449,53 @@ proc semOld(c: PContext; n: PNode): PNode =
localError(c.config, n[1].info, n[1].sym.name.s & " does not belong to " & getCurrOwner(c).name.s)
result = n
proc semNewFinalize(c: PContext; n: PNode): PNode =
# Make sure the finalizer procedure refers to a procedure
if n[^1].kind == nkSym and n[^1].sym.kind notin {skProc, skFunc}:
localError(c.config, n.info, "finalizer must be a direct reference to a proc")
elif optTinyRtti in c.config.globalOptions:
let nfin = skipConvCastAndClosure(n[^1])
let fin = case nfin.kind
of nkSym: nfin.sym
of nkLambda, nkDo: nfin[namePos].sym
else:
localError(c.config, n.info, "finalizer must be a direct reference to a proc")
nil
if fin != nil:
if fin.kind notin {skProc, skFunc}:
# calling convention is checked in codegen
localError(c.config, n.info, "finalizer must be a direct reference to a proc")
# check if we converted this finalizer into a destructor already:
let t = whereToBindTypeHook(c, fin.typ[1].skipTypes(abstractInst+{tyRef}))
if t != nil and getAttachedOp(c.graph, t, attachedDestructor) != nil and
getAttachedOp(c.graph, t, attachedDestructor).owner == fin:
discard "already turned this one into a finalizer"
else:
if sfForward in fin.flags:
let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), nextSymId c.idgen, fin.owner, fin.info)
let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, nextSymId c.idgen))
wrapperSym.flags.incl sfUsed
let wrapper = c.semExpr(c, newProcNode(nkProcDef, fin.info, body = newTree(nkCall, newSymNode(fin), selfSymNode),
params = nkFormalParams.newTree(c.graph.emptyNode,
newTree(nkIdentDefs, selfSymNode, fin.ast[paramsPos][1][1], c.graph.emptyNode)
),
name = newSymNode(wrapperSym), pattern = c.graph.emptyNode,
genericParams = c.graph.emptyNode, pragmas = c.graph.emptyNode, exceptions = c.graph.emptyNode), {})
var transFormedSym = turnFinalizerIntoDestructor(c, wrapperSym, wrapper.info)
transFormedSym.owner = fin
if c.config.backend == backendCpp or sfCompileToCpp in c.module.flags:
let origParamType = transFormedSym.ast[bodyPos][1].typ
let selfSymbolType = makePtrType(c, origParamType.skipTypes(abstractPtrs))
let selfPtr = newNodeI(nkHiddenAddr, transFormedSym.ast[bodyPos][1].info)
selfPtr.add transFormedSym.ast[bodyPos][1]
selfPtr.typ = selfSymbolType
transFormedSym.ast[bodyPos][1] = c.semExpr(c, selfPtr)
bindTypeHook(c, transFormedSym, n, attachedDestructor)
else:
bindTypeHook(c, turnFinalizerIntoDestructor(c, fin, n.info), n, attachedDestructor)
result = n
proc semPrivateAccess(c: PContext, n: PNode): PNode =
let t = n[1].typ[0].toObjectFromRefPtrGeneric
c.currentScope.allowPrivateAccess.add t.sym
@@ -513,30 +560,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
else:
result = plugin(c, n)
of mNewFinalize:
# Make sure the finalizer procedure refers to a procedure
if n[^1].kind == nkSym and n[^1].sym.kind notin {skProc, skFunc}:
localError(c.config, n.info, "finalizer must be a direct reference to a proc")
elif optTinyRtti in c.config.globalOptions:
let nfin = skipConvCastAndClosure(n[^1])
let fin = case nfin.kind
of nkSym: nfin.sym
of nkLambda, nkDo: nfin[namePos].sym
else:
localError(c.config, n.info, "finalizer must be a direct reference to a proc")
nil
if fin != nil:
if fin.kind notin {skProc, skFunc}:
# calling convention is checked in codegen
localError(c.config, n.info, "finalizer must be a direct reference to a proc")
# check if we converted this finalizer into a destructor already:
let t = whereToBindTypeHook(c, fin.typ[1].skipTypes(abstractInst+{tyRef}))
if t != nil and getAttachedOp(c.graph, t, attachedDestructor) != nil and
getAttachedOp(c.graph, t, attachedDestructor).owner == fin:
discard "already turned this one into a finalizer"
else:
bindTypeHook(c, turnFinalizerIntoDestructor(c, fin, n.info), n, attachedDestructor)
result = n
result = semNewFinalize(c, n)
of mDestroy:
result = n
let t = n[1].typ.skipTypes(abstractVar)

View File

@@ -67,10 +67,11 @@ type
exc: PNode # stack of exceptions
tags: PNode # list of tags
forbids: PNode # list of tags
bottom, inTryStmt, inExceptOrFinallyStmt, leftPartOfAsgn, inIfStmt: int
bottom, inTryStmt, inExceptOrFinallyStmt, leftPartOfAsgn, inIfStmt, currentBlock: int
owner: PSym
ownerModule: PSym
init: seq[int] # list of initialized variables
scopes: Table[int, int] # maps var-id to its scope (see also `currentBlock`).
guards: TModel # nested guards
locked: seq[PNode] # locked locations
gcUnsafe, isRecursive, isTopLevel, hasSideEffect, inEnforcedGcSafe: bool
@@ -189,6 +190,10 @@ proc makeVolatile(a: PEffects; s: PSym) {.inline.} =
if a.inTryStmt > 0 and a.config.exc == excSetjmp:
incl(s.flags, sfVolatile)
proc varDecl(a: PEffects; n: PNode) {.inline.} =
if n.kind == nkSym:
a.scopes[n.sym.id] = a.currentBlock
proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
if n.kind != nkSym: return
let s = n.sym
@@ -197,6 +202,23 @@ proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
for x in a.init:
if x == s.id: return
a.init.add s.id
if a.scopes.getOrDefault(s.id) == a.currentBlock:
#[ Consider this case:
var x: T
while true:
if cond:
x = T() #1
else:
x = T() #2
use x
Even though both #1 and #2 are first writes we must use the `=copy`
here so that the old value is destroyed because `x`'s destructor is
run outside of the while loop. This is why we need the check here that
the assignment is done in the same logical block as `x` was declared in.
]#
n.flags.incl nfFirstWrite2
proc initVarViaNew(a: PEffects, n: PNode) =
if n.kind != nkSym: return
@@ -1101,18 +1123,23 @@ proc track(tracked: PEffects, n: PNode) =
for i in 0..<child.len-2:
createTypeBoundOps(tracked, child[i].typ, child.info)
else:
createTypeBoundOps(tracked, child[0].typ, child.info)
if child.kind == nkIdentDefs and last.kind != nkEmpty:
createTypeBoundOps(tracked, skipPragmaExpr(child[0]).typ, child.info)
if child.kind == nkIdentDefs:
for i in 0..<child.len-2:
initVar(tracked, child[i], volatileCheck=false)
addAsgnFact(tracked.guards, child[i], last)
notNilCheck(tracked, last, child[i].typ)
elif child.kind == nkVarTuple and last.kind != nkEmpty:
let a = skipPragmaExpr(child[i])
varDecl(tracked, a)
if last.kind != nkEmpty:
initVar(tracked, a, volatileCheck=false)
addAsgnFact(tracked.guards, a, last)
notNilCheck(tracked, last, a.typ)
elif child.kind == nkVarTuple:
for i in 0..<child.len-1:
if child[i].kind == nkEmpty or
child[i].kind == nkSym and child[i].sym.name.s == "_":
continue
initVar(tracked, child[i], volatileCheck=false)
varDecl(tracked, child[i])
if last.kind != nkEmpty:
initVar(tracked, child[i], volatileCheck=false)
if last.kind in {nkPar, nkTupleConstr}:
addAsgnFact(tracked.guards, child[i], last[i])
notNilCheck(tracked, last[i], child[i].typ)
@@ -1127,6 +1154,7 @@ proc track(tracked: PEffects, n: PNode) =
of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n[1])
of nkWhileStmt:
# 'while true' loop?
inc tracked.currentBlock
if isTrue(n[0]):
trackBlock(tracked, n[1])
else:
@@ -1138,8 +1166,10 @@ proc track(tracked: PEffects, n: PNode) =
track(tracked, n[1])
setLen(tracked.init, oldState)
setLen(tracked.guards.s, oldFacts)
dec tracked.currentBlock
of nkForStmt, nkParForStmt:
# we are very conservative here and assume the loop is never executed:
inc tracked.currentBlock
let oldState = tracked.init.len
let oldFacts = tracked.guards.s.len
@@ -1181,6 +1211,8 @@ proc track(tracked: PEffects, n: PNode) =
track(tracked, loopBody)
setLen(tracked.init, oldState)
setLen(tracked.guards.s, oldFacts)
dec tracked.currentBlock
of nkObjConstr:
when false: track(tracked, n[0])
let oldFacts = tracked.guards.s.len
@@ -1434,6 +1466,7 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PC
t.graph = g
t.config = g.config
t.c = c
t.currentBlock = 1
proc hasRealBody(s: PSym): bool =
## also handles importc procs with runnableExamples, which requires `=`,
@@ -1454,6 +1487,12 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
var t: TEffects
initEffects(g, inferredEffects, s, t, c)
rawInitEffects g, effects
if not isEmptyType(s.typ[0]) and
s.kind in {skProc, skFunc, skConverter, skMethod}:
var res = s.ast[resultPos].sym # get result symbol
t.scopes[res.id] = t.currentBlock
track(t, body)
if s.kind != skMacro:

View File

@@ -225,7 +225,7 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
if a.len == 2 and a[0].kind == nkBracket:
# rewrite ``except [a, b, c]: body`` -> ```except a, b, c: body```
a.sons[0..0] = a[0].sons
a.sons[0..0] = move a[0].sons
if a.len == 2 and a[0].isInfixAs():
# support ``except Exception as ex: body``
@@ -574,9 +574,13 @@ proc semVarMacroPragma(c: PContext, a: PNode, n: PNode): PNode =
pragma(c, defs[lhsPos][namePos].sym, defs[lhsPos][pragmaPos], validPragmas)
return result
proc errorSymChoiceUseQualifier(c: PContext; n: PNode) =
proc msgSymChoiceUseQualifier(c: PContext; n: PNode; note = errGenerated) =
assert n.kind in nkSymChoices
var err = "ambiguous identifier: '" & $n[0] & "'"
var err =
if note == hintAmbiguousEnum:
"ambiguous enum field '$1' assumed to be of type $2, this will become an error in the future" % [$n[0], typeToString(n[0].typ)]
else:
"ambiguous identifier: '" & $n[0] & "'"
var i = 0
for child in n:
let candidate = child.sym
@@ -584,7 +588,7 @@ proc errorSymChoiceUseQualifier(c: PContext; n: PNode) =
else: err.add "\n"
err.add " " & candidate.owner.name.s & "." & candidate.name.s
inc i
localError(c.config, n.info, errGenerated, err)
message(c.config, n.info, note, err)
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
var b: PNode
@@ -611,8 +615,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
if a[^1].kind != nkEmpty:
def = semExprWithType(c, a[^1], {}, typ)
if def.kind in nkSymChoices and def[0].typ.skipTypes(abstractInst).kind == tyEnum:
errorSymChoiceUseQualifier(c, def)
if def.kind in nkSymChoices and def[0].sym.kind == skEnumField:
msgSymChoiceUseQualifier(c, def, errGenerated)
elif def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}:
typFlags.incl taIsTemplateOrMacro
elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
@@ -694,28 +698,25 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
if def.kind != nkEmpty:
if sfThread in v.flags: localError(c.config, def.info, errThreadvarCannotInit)
setVarType(c, v, typ)
# this is needed for the evaluation pass, guard checking
# and custom pragmas:
b = newNodeI(nkIdentDefs, a.info)
if importantComments(c.config):
# keep documentation information:
b.comment = a.comment
b.add newSymNode(v)
# postfix not generated here (to generate, get rid of it in transf)
if a[j].kind == nkPragmaExpr:
var p = newNodeI(nkPragmaExpr, a.info)
p.add newSymNode(v)
p.add a[j][1]
b.add p
else:
b.add newSymNode(v)
# keep type desc for doc generator
b.add a[^2]
b.add copyTree(def)
addToVarSection(c, result, n, b)
# this is needed for the evaluation pass, guard checking
# and custom pragmas:
var ast = newNodeI(nkIdentDefs, a.info)
if a[j].kind == nkPragmaExpr:
var p = newNodeI(nkPragmaExpr, a.info)
p.add newSymNode(v)
p.add a[j][1].copyTree
ast.add p
else:
ast.add newSymNode(v)
ast.add a[^2].copyTree
ast.add def
v.ast = ast
v.ast = b
else:
if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j]
# bug #7663, for 'nim check' this can be a non-tuple:
@@ -811,12 +812,21 @@ proc semConst(c: PContext, n: PNode): PNode =
if a.kind != nkVarTuple:
setVarType(c, v, typ)
v.ast = def # no need to copy
when false:
v.ast = def # no need to copy
b = newNodeI(nkConstDef, a.info)
if importantComments(c.config): b.comment = a.comment
b.add newSymNode(v)
# postfix not generated here (to generate, get rid of it in transf)
if a[j].kind == nkPragmaExpr:
var p = newNodeI(nkPragmaExpr, a.info)
p.add newSymNode(v)
p.add a[j][1].copyTree
b.add p
else:
b.add newSymNode(v)
b.add a[1]
b.add copyTree(def)
v.ast = b
else:
setVarType(c, v, typ[j])
v.ast = if def[j].kind != nkExprColonExpr: def[j]
@@ -2330,6 +2340,11 @@ proc setLine(n: PNode, info: TLineInfo) =
for i in 0..<n.safeLen: setLine(n[i], info)
n.info = info
proc recursiveSetFlag(n: PNode, flag: TNodeFlag) =
if n != nil:
for i in 0..<n.safeLen: recursiveSetFlag(n[i], flag)
incl(n.flags, flag)
proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
checkSonsLen(n, 2, c.config)
let pragmaList = n[0]
@@ -2354,7 +2369,7 @@ proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
for i in 0..<pragmaList.len:
case whichPragma(pragmaList[i])
of wLine: setLine(result, pragmaList[i].info)
of wNoRewrite: incl(result.flags, nfNoRewrite)
of wNoRewrite: recursiveSetFlag(result, nfNoRewrite)
else: discard
proc semStaticStmt(c: PContext, n: PNode): PNode =

View File

@@ -554,6 +554,8 @@ proc semBranchRange(c: PContext, t, a, b: PNode, covered: var Int128): PNode =
checkMinSonsLen(t, 1, c.config)
let ac = semConstExpr(c, a)
let bc = semConstExpr(c, b)
if ac.kind in {nkStrLit..nkTripleStrLit} or bc.kind in {nkStrLit..nkTripleStrLit}:
localError(c.config, b.info, "range of string is invalid")
let at = fitNode(c, t[0].typ, ac, ac.info).skipConvTakeType
let bt = fitNode(c, t[0].typ, bc, bc.info).skipConvTakeType
@@ -591,7 +593,9 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
branch[i] = semCaseBranchRange(c, t, b, covered)
else:
# constant sets and arrays are allowed:
var r = semConstExpr(c, b)
# set expected type to selector type for type inference
# even if it can be a different type like a set or array
var r = semConstExpr(c, b, expectedType = t[0].typ)
if r.kind in {nkCurly, nkBracket} and r.len == 0 and branch.len == 2:
# discarding ``{}`` and ``[]`` branches silently
delSon(branch, 0)
@@ -824,7 +828,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
f.options = c.config.options
if fieldOwner != nil and
{sfImportc, sfExportc} * fieldOwner.flags != {} and
not hasCaseFields and f.loc.r == nil:
not hasCaseFields and f.loc.r == "":
f.loc.r = rope(f.name.s)
f.flags.incl {sfImportc, sfExportc} * fieldOwner.flags
inc(pos)

View File

@@ -21,8 +21,7 @@ proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len)
proc `&=`(c: var MD5Context, ch: char) =
# XXX suspicious code here; relies on ch being zero terminated?
md5Update(c, unsafeAddr ch, 1)
proc `&=`(c: var MD5Context, r: Rope) =
for l in leaves(r): md5Update(c, l.cstring, l.len)
proc `&=`(c: var MD5Context, i: BiggestInt) =
md5Update(c, cast[cstring](unsafeAddr i), sizeof(i))
proc `&=`(c: var MD5Context, f: BiggestFloat) =
@@ -150,7 +149,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
# is actually safe without an infinite recursion check:
if t.sym != nil:
if {sfCompilerProc} * t.sym.flags != {}:
doAssert t.sym.loc.r != nil
doAssert t.sym.loc.r != ""
# The user has set a specific name for this type
c &= t.sym.loc.r
elif CoOwnerSig in flags:
@@ -334,7 +333,7 @@ proc hashVarSymBody(graph: ModuleGraph, c: var MD5Context, s: PSym) =
c &= hashNonProc(s)
# this one works for let and const but not for var. True variables can change value
# later on. it is user resposibility to hash his global state if required
if s.ast != nil and s.ast.kind == nkIdentDefs:
if s.ast != nil and s.ast.kind in {nkIdentDefs, nkConstDef}:
hashBodyTree(graph, c, s.ast[^1])
else:
hashBodyTree(graph, c, s.ast)

View File

@@ -535,16 +535,6 @@ proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; i
if parentFileIndex == conf.m.trackPos.fileIndex:
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
proc extractPragma(s: PSym): PNode =
if s.kind in routineKinds:
result = s.ast[pragmasPos]
elif s.kind in {skType, skVar, skLet}:
if s.ast != nil and s.ast.len > 0:
if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1:
# s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma]
result = s.ast[0][1]
doAssert result == nil or result.kind == nkPragma
proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) =
var pragmaNode: PNode
pragmaNode = if s.kind == skEnumField: extractPragma(s.owner) else: extractPragma(s)

View File

@@ -104,9 +104,11 @@ proc transformSons(c: PTransf, n: PNode): PNode =
for i in 0..<n.len:
result[i] = transform(c, n[i])
proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PNode): PNode =
proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PNode; isFirstWrite: bool): PNode =
result = newTransNode(kind, ri.info, 2)
result[0] = le
if isFirstWrite:
le.flags.incl nfFirstWrite2
result[1] = ri
proc transformSymAux(c: PTransf, n: PNode): PNode =
@@ -184,10 +186,12 @@ proc transformVarSection(c: PTransf, v: PNode): PNode =
if it.kind == nkCommentStmt:
result[i] = it
elif it.kind == nkIdentDefs:
if it[0].kind == nkSym:
var vn = it[0]
if vn.kind == nkPragmaExpr: vn = vn[0]
if vn.kind == nkSym:
internalAssert(c.graph.config, it.len == 3)
let x = freshVar(c, it[0].sym)
idNodeTablePut(c.transCon.mapping, it[0].sym, x)
let x = freshVar(c, vn.sym)
idNodeTablePut(c.transCon.mapping, vn.sym, x)
var defs = newTransNode(nkIdentDefs, it.info, 3)
if importantComments(c.graph.config):
# keep documentation information:
@@ -363,9 +367,9 @@ proc transformYield(c: PTransf, n: PNode): PNode =
case lhs.kind
of nkSym:
internalAssert c.graph.config, lhs.sym.kind == skForVar
result = newAsgnStmt(c, nkFastAsgn, lhs, rhs)
result = newAsgnStmt(c, nkFastAsgn, lhs, rhs, false)
of nkDotExpr:
result = newAsgnStmt(c, nkAsgn, lhs, rhs)
result = newAsgnStmt(c, nkAsgn, lhs, rhs, false)
else:
internalAssert c.graph.config, false
result = newTransNode(nkStmtList, n.info, 0)
@@ -732,7 +736,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =
# generate a temporary and produce an assignment statement:
var temp = newTemp(c, t, formal.info)
addVar(v, temp)
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg))
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg, true))
idNodeTablePut(newC.mapping, formal, temp)
of paVarAsgn:
assert(skipTypes(formal.typ, abstractInst).kind in {tyVar})
@@ -742,7 +746,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =
# arrays will deep copy here (pretty bad).
var temp = newTemp(c, arg.typ, formal.info)
addVar(v, temp)
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg))
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg, true))
idNodeTablePut(newC.mapping, formal, temp)
let body = transformBody(c.graph, c.idgen, iter, true)
@@ -1036,7 +1040,7 @@ proc transform(c: PTransf, n: PNode): PNode =
result = transformAsgn(c, n)
of nkIdentDefs, nkConstDef:
result = newTransNode(n)
result[0] = transform(c, n[0])
result[0] = transform(c, skipPragmaExpr(n[0]))
# Skip the second son since it only contains an unsemanticized copy of the
# variable type used by docgen
let last = n.len-1

View File

@@ -1572,6 +1572,7 @@ proc getProcConvMismatch*(c: ConfigRef, f, a: PType, rel = isNone): (set[ProcCon
of isInferred: result[1] = isInferredConvertible
of isBothMetaConvertible: result[1] = isBothMetaConvertible
elif result[1] != isNone: result[1] = isConvertible
else: result[0].incl pcmDifferentCallConv
else:
result[1] = isNone
result[0].incl pcmDifferentCallConv
@@ -1607,6 +1608,25 @@ proc addPragmaAndCallConvMismatch*(message: var string, formal, actual: PType, c
expectedPragmas.setLen(max(0, expectedPragmas.len - 2)) # Remove ", "
message.add "\n Pragma mismatch: got '{.$1.}', but expected '{.$2.}'." % [gotPragmas, expectedPragmas]
proc processPragmaAndCallConvMismatch(msg: var string, formal, actual: PType, conf: ConfigRef) =
if formal.kind == tyProc and actual.kind == tyProc:
msg.addPragmaAndCallConvMismatch(formal, actual, conf)
case compatibleEffects(formal, actual)
of efCompat: discard
of efRaisesDiffer:
msg.add "\n.raise effects differ"
of efRaisesUnknown:
msg.add "\n.raise effect is 'can raise any'"
of efTagsDiffer:
msg.add "\n.tag effects differ"
of efTagsUnknown:
msg.add "\n.tag effect is 'any tag allowed'"
of efLockLevelsDiffer:
msg.add "\nlock levels differ"
of efEffectsDelayed:
msg.add "\n.effectsOf annotations differ"
of efTagsIllegal:
msg.add "\n.notTag catched an illegal effect"
proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: PNode) =
if formal.kind != tyError and actual.kind != tyError:
@@ -1626,25 +1646,18 @@ proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: P
msg.add "\n"
msg.add " but expected '$1'" % x
if verbose: msg.addDeclaredLoc(conf, formal)
if formal.kind == tyProc and actual.kind == tyProc:
msg.addPragmaAndCallConvMismatch(formal, actual, conf)
case compatibleEffects(formal, actual)
of efCompat: discard
of efRaisesDiffer:
msg.add "\n.raise effects differ"
of efRaisesUnknown:
msg.add "\n.raise effect is 'can raise any'"
of efTagsDiffer:
msg.add "\n.tag effects differ"
of efTagsUnknown:
msg.add "\n.tag effect is 'any tag allowed'"
of efLockLevelsDiffer:
msg.add "\nlock levels differ"
of efEffectsDelayed:
msg.add "\n.effectsOf annotations differ"
of efTagsIllegal:
msg.add "\n.notTag catched an illegal effect"
var a = formal
var b = actual
if formal.kind == tyArray and actual.kind == tyArray:
a = formal[1]
b = actual[1]
processPragmaAndCallConvMismatch(msg, a, b, conf)
elif formal.kind == tySequence and actual.kind == tySequence:
a = formal[0]
b = actual[0]
processPragmaAndCallConvMismatch(msg, a, b, conf)
else:
processPragmaAndCallConvMismatch(msg, a, b, conf)
localError(conf, info, msg)
proc isTupleRecursive(t: PType, cycleDetector: var IntSet): bool =

View File

@@ -580,24 +580,33 @@ proc borrowingAsgn(c: var Partitions; dest, src: PNode) =
if dest.kind == nkSym:
if directViewType(dest.typ) != noView:
borrowFrom(c, dest.sym, src)
elif dest.kind in {nkHiddenDeref, nkDerefExpr, nkBracketExpr}:
case directViewType(dest[0].typ)
of mutableView, immutableView:
# we do not borrow, but we use the view to mutate the borrowed
# location:
let viewOrigin = pathExpr(dest, c.owner)
if viewOrigin.kind == nkSym:
let vid = variableId(c, viewOrigin.sym)
else:
let viewOrigin = pathExpr(dest, c.owner)
if viewOrigin != nil and viewOrigin.kind == nkSym:
let viewSym = viewOrigin.sym
let directView = directViewType(dest[0].typ) # check something like result[first] = toOpenArray(s, first, last-1)
# so we don't need to iterate the original type
let originSymbolView = directViewType(viewSym.typ) # find the original symbol which preserves the view type
# var foo: var Object = a
# foo.id = 777 # the type of foo is no view, so we need
# to check the original symbol
let viewSets = {directView, originSymbolView}
if viewSets * {mutableView, immutableView} != {}:
# we do not borrow, but we use the view to mutate the borrowed
# location:
let vid = variableId(c, viewSym)
if vid >= 0:
c.s[vid].flags.incl viewDoesMutate
#[of immutableView:
if dest.kind == nkBracketExpr and dest[0].kind == nkHiddenDeref and
mutableParameter(dest[0][0]):
discard "remains a mutable location anyhow"
#[of immutableView:
if dest.kind == nkBracketExpr and dest[0].kind == nkHiddenDeref and
mutableParameter(dest[0][0]):
discard "remains a mutable location anyhow"
else:
localError(c.g.config, dest.info, "attempt to mutate a borrowed location from an immutable view")
]#
else:
localError(c.g.config, dest.info, "attempt to mutate a borrowed location from an immutable view")
]#
of noView: discard "nothing to do"
discard "nothing to do"
proc containsPointer(t: PType): bool =
proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr}
@@ -767,6 +776,11 @@ proc traverse(c: var Partitions; n: PNode) =
# mutate(graph)
# connect(graph, cursorVar)
for child in n: traverse(c, child)
if n.kind == nkWhileStmt:
traverse(c, n[0])
# variables in while condition has longer alive time than local variables
# in the while loop body
else:
for child in n: traverse(c, child)
@@ -854,6 +868,11 @@ proc computeLiveRanges(c: var Partitions; n: PNode) =
inc c.inLoop
for child in n: computeLiveRanges(c, child)
dec c.inLoop
if n.kind == nkWhileStmt:
computeLiveRanges(c, n[0])
# variables in while condition has longer alive time than local variables
# in the while loop body
of nkElifBranch, nkElifExpr, nkElse, nkOfBranch:
inc c.inConditional
for child in n: computeLiveRanges(c, child)

View File

@@ -2000,7 +2000,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
elif importcCond(c, s): c.importcSym(n.info, s)
genLit(c, n, dest)
of skConst:
let constVal = if s.ast != nil: s.ast else: s.typ.n
let constVal = if s.astdef != nil: s.astdef else: s.typ.n
gen(c, constVal, dest)
of skEnumField:
# we never reach this case - as of the time of this comment,

View File

@@ -15,6 +15,10 @@ cc = gcc
--parallel_build: "0" # 0 to auto-detect number of processors
hint[LineTooLong]=off
@if nimHasAmbiguousEnumHint:
# not needed if hint is a style check
hint[AmbiguousEnum]=off
@end
#hint[XDeclaredButNotUsed]=off
threads:on

View File

@@ -127,8 +127,7 @@ Advanced options:
--skipParentCfg:on|off do not read the parent dirs' configuration files
--skipProjCfg:on|off do not read the project's configuration file
--mm:orc|arc|refc|markAndSweep|boehm|go|none|regions
select which memory management to use; default is 'refc'
recommended is 'orc'
select which memory management to use; default is 'orc'
--exceptions:setjmp|cpp|goto|quirky
select the exception handling implementation
--index:on|off turn index file generation on|off

View File

@@ -463,11 +463,7 @@ You can edit ``config/nimdoc.cfg`` and modify the ``doc.item.seesrc`` value
with a hyperlink to your own code repository.
In the case of Nim's own documentation, the `commit` value is just a commit
hash to append to a formatted URL to https://github.com/nim-lang/Nim. The
``tools/nimweb.nim`` helper queries the current git commit hash during the doc
generation, but since you might be working on an unpublished repository, it
also allows specifying a `githash` value in ``web/website.ini`` to force a
specific commit in the output.
hash to append to a formatted URL to https://github.com/nim-lang/Nim.
Other Input Formats

View File

@@ -695,6 +695,21 @@ are used for other notational purposes.
The `not` keyword is always a unary operator, `a not b` is parsed
as `a(not b)`, not as `(a) not (b)`.
Unicode Operators
-----------------
These Unicode operators are also parsed as operators::
∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ # same priority as * (multiplication)
± ⊕ ⊖ ⊞ ⊟ ⊔ # same priority as + (addition)
Unicode operators can be combined with non-Unicode operator
symbols. The usual precedence extensions then apply, for example, `⊠=` is an
assignment like operator just like `*=` is.
No Unicode normalization step is performed.
Other tokens
------------
@@ -1066,17 +1081,6 @@ operation meaning
`a %% b` unsigned integer modulo operation
`a <% b` treat `a` and `b` as unsigned and compare
`a <=% b` treat `a` and `b` as unsigned and compare
`ze(a)` extends the bits of `a` with zeros until it has the
width of the `int` type
`toU8(a)` treats `a` as unsigned and converts it to an
unsigned integer of 8 bits (but still the
`int8` type)
`toU16(a)` treats `a` as unsigned and converts it to an
unsigned integer of 16 bits (but still the
`int16` type)
`toU32(a)` treats `a` as unsigned and converts it to an
unsigned integer of 32 bits (but still the
`int32` type)
====================== ======================================================
`Automatic type conversion`:idx: is performed in expressions where different

View File

@@ -65,27 +65,6 @@ However, a `void` type cannot be inferred in generic code:
The `void` type is only valid for parameters and return types; other symbols
cannot have the type `void`.
Unicode Operators
=================
Under the `--experimental:unicodeOperators`:option: switch,
these Unicode operators are also parsed as operators::
∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ # same priority as * (multiplication)
± ⊕ ⊖ ⊞ ⊟ ⊔ # same priority as + (addition)
If enabled, Unicode operators can be combined with non-Unicode operator
symbols. The usual precedence extensions then apply, for example, `⊠=` is an
assignment like operator just like `*=` is.
No Unicode normalization step is performed.
.. note:: Due to parser limitations one **cannot** enable this feature via a
pragma `{.experimental: "unicodeOperators".}` reliably.
Top-down type inference
=======================

View File

@@ -28,10 +28,10 @@ To choose the memory management strategy use the `--mm:` switch.
ARC/ORC
-------
`--mm:orc` is a memory management mode primarily based on reference counting. Cycles
in the object graph are handled by a "cycle collector" which is based on "trial deletion".
Since algorithms based on "tracing" are not used, the runtime behavior is oblivious to
the involved heap sizes.
ORC is the default memory management strategy. It is a memory
management mode primarily based on reference counting. Reference cycles are
handled by a cycle collection mechanism based on "trial deletion".
Since algorithms based on "tracing" are not used, the runtime behavior is oblivious to the involved heap and stack sizes.
The reference counting operations (= "RC ops") do not use atomic instructions and do not have to --
instead entire subgraphs are *moved* between threads. The Nim compiler also aggressively
@@ -57,12 +57,11 @@ and leaks memory with `--mm:arc`, in other words, for `async` you need to use `-
Other MM modes
--------------
.. note:: The default `refc` GC is incremental, thread-local and not "stop-the-world".
.. note:: The `refc` GC is incremental, thread-local and not "stop-the-world".
--mm:refc This is the default memory management strategy. It's a
deferred reference counting based garbage collector
with a simple Mark&Sweep backup GC in order to collect cycles. Heaps are thread-local.
[This document](refc.html) contains further information.
--mm:refc It's a deferred reference counting based garbage collector
with a simple Mark&Sweep backup GC in order to collect cycles.
Heaps are thread-local. [This document](refc.html) contains further information.
--mm:markAndSweep Simple Mark-And-Sweep based garbage collector.
Heaps are thread-local.
--mm:boehm Boehm based garbage collector, it offers a shared heap.

View File

@@ -34,6 +34,9 @@ import std/[os, strutils, parseopt, osproc]
# If this fails with: `Error: cannot open file: std/os`, see
# https://github.com/nim-lang/Nim/pull/14291 for explanation + how to fix.
when defined(nimPreviewSlimSystem):
import std/[assertions, syncio]
import tools / kochdocs
import tools / deps
@@ -89,9 +92,6 @@ Commands for core developers:
tests [options] run the testsuite (run a subset of tests by
specifying a category, e.g. `tests cat async`)
temp options creates a temporary compiler for testing
Web options:
--googleAnalytics:UA-... add the given google analytics code to the docs. To
build the official docs, use UA-48159761-1
"""
let kochExe* = when isMainModule: os.getAppFilename() # always correct when koch is main program, even if `koch` exe renamed e.g.: `nim c -o:koch_debug koch.nim`
@@ -150,7 +150,7 @@ proc bundleNimbleExe(latest: bool, args: string) =
commit = commit, allowBundled = true)
# installer.ini expects it under $nim/bin
nimCompile("dist/nimble/src/nimble.nim",
options = "-d:release --useVersion:1.6 --noNimblePath " & args)
options = "-d:release --mm:refc --useVersion:1.6 --noNimblePath " & args)
proc bundleNimsuggest(args: string) =
nimCompileFold("Compile nimsuggest", "nimsuggest/nimsuggest.nim",
@@ -251,20 +251,6 @@ proc install(args: string) =
geninstall()
exec("sh ./install.sh $#" % args)
when false:
proc web(args: string) =
nimexec("js tools/dochack/dochack.nim")
nimexec("cc -r tools/nimweb.nim $# web/website.ini --putenv:nimversion=$#" %
[args, VersionAsString])
proc website(args: string) =
nimexec("cc -r tools/nimweb.nim $# --website web/website.ini --putenv:nimversion=$#" %
[args, VersionAsString])
proc pdf(args="") =
exec("$# cc -r tools/nimweb.nim $# --pdf web/website.ini --putenv:nimversion=$#" %
[findNim().quoteShell(), args, VersionAsString], additionalPATH=findNim().splitFile.dir)
# -------------- boot ---------------------------------------------------------
proc findStartNim: string =
@@ -548,7 +534,8 @@ proc runCI(cmd: string) =
# boot without -d:nimHasLibFFI to make sure this still works
# `--lib:lib` is needed for bootstrap on openbsd, for reasons described in
# https://github.com/nim-lang/Nim/pull/14291 (`getAppFilename` bugsfor older nim on openbsd).
kochExecFold("Boot in release mode", "boot -d:release -d:nimStrictMode --lib:lib")
kochExecFold("Boot in release mode", "boot -d:release --gc:refc -d:nimStrictMode --lib:lib")
kochExecFold("Boot Nim ORC", "boot -d:release --lib:lib")
when false: # debugging: when you need to run only 1 test in CI, use something like this:
execFold("debugging test", "nim r tests/stdlib/tosproc.nim")
@@ -602,10 +589,6 @@ proc runCI(cmd: string) =
execFold("Run atlas tests", "nim c -r -d:atlasTests tools/atlas/atlas.nim clone https://github.com/disruptek/balls")
when not defined(bsd):
# the BSDs are overwhelmed already, so only run this test on the other machines:
kochExecFold("Boot Nim ORC", "boot -d:release --mm:orc --lib:lib")
proc testUnixInstall(cmdLineRest: string) =
csource("-d:danger" & cmdLineRest)
xz(false, cmdLineRest)
@@ -693,7 +676,7 @@ when isMainModule:
case normalize(op.key)
of "boot": boot(op.cmdLineRest)
of "clean": clean(op.cmdLineRest)
of "doc", "docs": buildDocs(op.cmdLineRest, localDocsOnly, localDocsOut)
of "doc", "docs": buildDocs(op.cmdLineRest & paCode, localDocsOnly, localDocsOut)
of "doc0", "docs0":
# undocumented command for Araq-the-merciful:
buildDocs(op.cmdLineRest & gaCode)

View File

@@ -1567,7 +1567,7 @@ proc customPragmaNode(n: NimNode): NimNode =
let impl = n.getImpl()
if impl.kind in RoutineNodes:
return impl.pragma
elif impl.kind == nnkIdentDefs and impl[0].kind == nnkPragmaExpr:
elif impl.kind in {nnkIdentDefs, nnkConstDef} and impl[0].kind == nnkPragmaExpr:
return impl[0][1]
else:
let timpl = typ.getImpl()

View File

@@ -40,6 +40,10 @@ include "system/hti.nim"
{.pop.}
when defined(nimPreviewSlimSystem):
import std/assertions
type
AnyKind* = enum ## The kind of `Any`.
akNone = 0, ## invalid
@@ -87,7 +91,7 @@ type
rawTypePtr: pointer
ppointer = ptr pointer
pbyteArray = ptr array[0xffff, int8]
pbyteArray = ptr array[0xffff, uint8]
when not defined(gcDestructors):
type
@@ -135,10 +139,10 @@ proc getDiscriminant(aa: pointer, n: ptr TNimNode): int =
var d: int
let a = cast[ByteAddress](aa)
case n.typ.size
of 1: d = ze(cast[ptr int8](a +% n.offset)[])
of 2: d = ze(cast[ptr int16](a +% n.offset)[])
of 4: d = int(cast[ptr int32](a +% n.offset)[])
of 8: d = int(cast[ptr int64](a +% n.offset)[])
of 1: d = int(cast[ptr uint8](a +% n.offset)[])
of 2: d = int(cast[ptr uint16](a +% n.offset)[])
of 4: d = int(cast[ptr uint32](a +% n.offset)[])
of 8: d = int(cast[ptr uint64](a +% n.offset)[])
else: assert(false)
return d
@@ -480,8 +484,8 @@ proc getBiggestInt*(x: Any): BiggestInt =
of tyChar: result = BiggestInt(cast[ptr char](x.value)[])
of tyEnum, tySet:
case t.size
of 1: result = ze64(cast[ptr int8](x.value)[])
of 2: result = ze64(cast[ptr int16](x.value)[])
of 1: result = int64(cast[ptr uint8](x.value)[])
of 2: result = int64(cast[ptr uint16](x.value)[])
of 4: result = BiggestInt(cast[ptr int32](x.value)[])
of 8: result = BiggestInt(cast[ptr int64](x.value)[])
else: assert false
@@ -505,8 +509,8 @@ proc setBiggestInt*(x: Any, y: BiggestInt) =
of tyChar: cast[ptr char](x.value)[] = chr(y.int)
of tyEnum, tySet:
case t.size
of 1: cast[ptr int8](x.value)[] = toU8(y.int)
of 2: cast[ptr int16](x.value)[] = toU16(y.int)
of 1: cast[ptr uint8](x.value)[] = uint8(y.int)
of 2: cast[ptr uint16](x.value)[] = uint16(y.int)
of 4: cast[ptr int32](x.value)[] = int32(y)
of 8: cast[ptr int64](x.value)[] = y
else: assert false
@@ -687,14 +691,14 @@ iterator elements*(x: Any): int =
# "typ.slots.len" field is for sets the "first" field
var u: int64
case typ.size
of 1: u = ze64(cast[ptr int8](p)[])
of 2: u = ze64(cast[ptr int16](p)[])
of 4: u = ze64(cast[ptr int32](p)[])
of 1: u = int64(cast[ptr uint8](p)[])
of 2: u = int64(cast[ptr uint16](p)[])
of 4: u = int64(cast[ptr uint32](p)[])
of 8: u = cast[ptr int64](p)[]
else:
let a = cast[pbyteArray](p)
for i in 0 .. typ.size*8-1:
if (ze(a[i div 8]) and (1 shl (i mod 8))) != 0:
if (int(a[i div 8]) and (1 shl (i mod 8))) != 0:
yield i + typ.node.len
if typ.size <= 8:
for i in 0..sizeof(int64)*8-1:
@@ -723,4 +727,4 @@ proc inclSetElement*(x: Any, elem: int) =
a[] = a[] or (1'i64 shl e)
else:
var a = cast[pbyteArray](p)
a[e shr 3] = toU8(a[e shr 3] or (1 shl (e and 7)))
a[e shr 3] = a[e shr 3] or uint8(1 shl (e and 7))

View File

@@ -45,6 +45,9 @@ jkl"""
import tables, strutils
when defined(nimPreviewSlimSystem):
import std/assertions
type
Item* = object ## An Item in the list of differences.
startA*: int ## Start Line number in Data A.

View File

@@ -90,7 +90,7 @@ import strutils, mysql
import db_common
export db_common
import std/private/since
import std/private/[since, dbutils]
type
DbConn* = distinct PMySQL ## encapsulates a database connection
@@ -138,14 +138,7 @@ proc dbQuote*(s: string): string =
add(result, '\'')
proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
result = ""
var a = 0
for c in items(string(formatstr)):
if c == '?':
add(result, dbQuote(args[a]))
inc(a)
else:
add(result, c)
dbFormatImpl(formatstr, dbQuote, args)
proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
tags: [ReadDbEffect, WriteDbEffect].} =
@@ -358,7 +351,7 @@ proc getValue*(db: DbConn, query: SqlQuery,
result = getRow(db, query, args)[0]
proc tryInsertId*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect], raises: [DbError].} =
## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error.
var q = dbFormat(query, args)
@@ -376,7 +369,7 @@ proc insertId*(db: DbConn, query: SqlQuery,
proc tryInsert*(db: DbConn, query: SqlQuery, pkName: string,
args: varargs[string, `$`]): int64
{.tags: [WriteDbEffect], raises: [], since: (1, 3).} =
{.tags: [WriteDbEffect], raises: [DbError], since: (1, 3).} =
## same as tryInsertID
tryInsertID(db, query, args)

View File

@@ -92,7 +92,7 @@ import strutils, odbcsql
import db_common
export db_common
import std/private/since
import std/private/[since, dbutils]
type
OdbcConnTyp = tuple[hDb: SqlHDBC, env: SqlHEnv, stmt: SqlHStmt]
@@ -197,14 +197,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string {.
noSideEffect.} =
## Replace any `?` placeholders with `args`,
## and quotes the arguments
result = ""
var a = 0
for c in items(string(formatstr)):
if c == '?':
add(result, dbQuote(args[a]))
inc(a)
else:
add(result, c)
dbFormatImpl(formatstr, dbQuote, args)
proc prepareFetch(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): TSqlSmallInt {.

View File

@@ -88,7 +88,7 @@ import strutils, postgres
import db_common
export db_common
import std/private/since
import std/private/[since, dbutils]
type
DbConn* = PPGconn ## encapsulates a database connection
@@ -116,19 +116,7 @@ proc dbQuote*(s: string): string =
add(result, '\'')
proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
result = ""
var a = 0
if args.len > 0 and not string(formatstr).contains("?"):
dbError("""parameter substitution expects "?" """)
if args.len == 0:
return string(formatstr)
else:
for c in items(string(formatstr)):
if c == '?':
add(result, dbQuote(args[a]))
inc(a)
else:
add(result, c)
dbFormatImpl(formatstr, dbQuote, args)
proc tryExec*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): bool {.tags: [ReadDbEffect, WriteDbEffect].} =

View File

@@ -172,6 +172,8 @@ import db_common
export db_common
import std/private/[since, dbutils]
when defined(nimPreviewSlimSystem):
import std/assertions
type
DbConn* = PSqlite3 ## Encapsulates a database connection.

View File

@@ -66,6 +66,9 @@ from strutils import `%`
import options
from unicode import runeLenAt
when defined(nimPreviewSlimSystem):
import std/assertions
export options
type

View File

@@ -34,6 +34,9 @@ runnableExamples:
import
pcre, strutils, rtarrays
when defined(nimPreviewSlimSystem):
import std/syncio
const
MaxSubpatterns* = 20
## defines the maximum number of subpatterns that can be captured.

View File

@@ -304,7 +304,7 @@ type
Stack* {.importc: "stack_t",
header: "<signal.h>", final, pure.} = object ## stack_t
ss_sp*: pointer ## Stack base or pointer.
ss_size*: csize ## Stack size.
ss_size*: csize_t ## Stack size.
ss_flags*: cint ## Flags.
SigInfo* {.importc: "siginfo_t",
@@ -404,7 +404,7 @@ type
IOVec* {.importc: "struct iovec", pure, final,
header: "<sys/uio.h>".} = object ## struct iovec
iov_base*: pointer ## Base address of a memory region for input or output.
iov_len*: csize ## The size of the memory pointed to by iov_base.
iov_len*: csize_t ## The size of the memory pointed to by iov_base.
Tmsghdr* {.importc: "struct msghdr", pure, final,
header: "<sys/socket.h>".} = object ## struct msghdr

View File

@@ -428,8 +428,8 @@ type
header: "<sys/socket.h>",
pure, final.} = object ## struct sockaddr_storage
ss_family*: TSa_Family ## Address family.
ss_padding: array[128 - sizeof(cshort) - sizeof(culong), char]
ss_align: clong
ss_padding {.importc: "__ss_padding".}: array[128 - sizeof(cshort) - sizeof(culong), char]
ss_align {.importc: "__ss_align".}: clong
Tif_nameindex* {.importc: "struct if_nameindex", final,
pure, header: "<net/if.h>".} = object ## struct if_nameindex

View File

@@ -286,7 +286,7 @@ type
header: "<signal.h>", final, pure.} = object ## stack_t
ss_sp*: pointer ## Stack base or pointer.
ss_flags*: cint ## Flags.
ss_size*: csize ## Stack size.
ss_size*: csize_t ## Stack size.
SigInfo* {.importc: "siginfo_t",
header: "<signal.h>", final, pure.} = object ## siginfo_t
@@ -321,7 +321,7 @@ type
aio_lio_opcode*: cint ## Operation to be performed.
aio_reqprio*: cint ## Request priority offset.
aio_buf*: pointer ## Location of buffer.
aio_nbytes*: csize ## Length of transfer.
aio_nbytes*: csize_t ## Length of transfer.
aio_sigevent*: SigEvent ## Signal number and value.
next_prio: pointer
abs_prio: cint
@@ -378,15 +378,15 @@ type
msg_name*: pointer ## Optional address.
msg_namelen*: SockLen ## Size of address.
msg_iov*: ptr IOVec ## Scatter/gather array.
msg_iovlen*: csize ## Members in msg_iov.
msg_iovlen*: csize_t ## Members in msg_iov.
msg_control*: pointer ## Ancillary data; see below.
msg_controllen*: csize ## Ancillary data buffer len.
msg_controllen*: csize_t ## Ancillary data buffer len.
msg_flags*: cint ## Flags on received message.
Tcmsghdr* {.importc: "struct cmsghdr", pure, final,
header: "<sys/socket.h>".} = object ## struct cmsghdr
cmsg_len*: csize ## Data byte count, including the cmsghdr.
cmsg_len*: csize_t ## Data byte count, including the cmsghdr.
cmsg_level*: cint ## Originating protocol.
cmsg_type*: cint ## Protocol-specific type.

View File

@@ -232,6 +232,9 @@ import asyncfutures except callSoon
import nativesockets, net, deques
when defined(nimPreviewSlimSystem):
import std/[assertions, syncio]
export Port, SocketFlag
export asyncfutures except callSoon
export asyncstreams

View File

@@ -24,6 +24,9 @@
import asyncdispatch, os
when defined(nimPreviewSlimSystem):
import std/[assertions, syncio]
# TODO: Fix duplication introduced by PR #4683.
when defined(windows) or defined(nimdoc):

View File

@@ -81,6 +81,9 @@
import asyncdispatch, asyncnet, nativesockets, strutils, parseutils, os, times
from net import BufferSize
when defined(nimPreviewSlimSystem):
import std/assertions
type
AsyncFtpClient* = ref object
csock*: AsyncSocket

View File

@@ -13,6 +13,7 @@ import system/stacktraces
when defined(nimPreviewSlimSystem):
import std/objectdollar # for StackTraceEntry
import std/assertions
# TODO: This shouldn't need to be included, but should ideally be exported.
type

View File

@@ -44,6 +44,9 @@ import httpcore
from nativesockets import getLocalAddr, Domain, AF_INET, AF_INET6
import std/private/since
when defined(nimPreviewSlimSystem):
import std/assertions
export httpcore except parseHeader
const

View File

@@ -96,6 +96,10 @@
##
import std/private/since
when defined(nimPreviewSlimSystem):
import std/[assertions, syncio]
import asyncdispatch, nativesockets, net, os
export SOBool

View File

@@ -11,6 +11,9 @@
import asyncfutures
when defined(nimPreviewSlimSystem):
import std/assertions
import deques
type

View File

@@ -161,7 +161,7 @@ proc encode*[T: SomeInteger|char](s: openArray[T], safe = false): string =
assert encode([1, 2, 3, 4, 5]) == "AQIDBAU="
encodeImpl()
proc encodeMime*(s: string, lineLen = 75, newLine = "\r\n"): string =
proc encodeMime*(s: string, lineLen = 75.Positive, newLine = "\r\n"): string =
## Encodes `s` into base64 representation as lines.
## Used in email MIME format, use `lineLen` and `newline`.
##
@@ -172,11 +172,25 @@ proc encodeMime*(s: string, lineLen = 75, newLine = "\r\n"): string =
## * `decode proc<#decode,string>`_ for decoding a string
runnableExamples:
assert encodeMime("Hello World", 4, "\n") == "SGVs\nbG8g\nV29y\nbGQ="
result = newStringOfCap(encodeSize(s.len))
for i, c in encode(s):
if i != 0 and (i mod lineLen == 0):
result.add(newLine)
result.add(c)
template cpy(l, src, idx) =
b = l
while i < b:
result[i] = src[idx]
inc i
inc idx
if s.len == 0: return
let e = encode(s)
if e.len <= lineLen or newLine.len == 0:
return e
result = newString(e.len + newLine.len * ((e.len div lineLen) - int(e.len mod lineLen == 0)))
var i, j, k, b: int
let nd = e.len - lineLen
while j < nd:
cpy(i + lineLen, e, j)
cpy(i + newLine.len, newLine, k)
k = 0
cpy(result.len, e, j)
proc initDecodeTable*(): array[256, char] =
# computes a decode table at compile time

View File

@@ -32,6 +32,9 @@
import strutils, os, strtabs, cookies, uri
export uri.encodeUrl, uri.decodeUrl
when defined(nimPreviewSlimSystem):
import std/syncio
proc addXmlChar(dest: var string, c: char) {.inline.} =
case c

View File

@@ -36,6 +36,9 @@ runnableExamples:
import std/private/since
when defined(nimPreviewSlimSystem):
import std/assertions
type
NodeObj[T] {.acyclic.} = object
byte: int ## byte index of the difference

View File

@@ -302,8 +302,7 @@ proc unzip*[S, T](s: openArray[(S, T)]): (seq[S], seq[T]) {.since: (1, 1).} =
unzipped2 = @['a', 'b', 'c']
assert zipped.unzip() == (unzipped1, unzipped2)
assert zip(unzipped1, unzipped2).unzip() == (unzipped1, unzipped2)
result[0] = newSeq[S](s.len)
result[1] = newSeq[T](s.len)
result = (newSeq[S](s.len), newSeq[T](s.len))
for i in 0..<s.len:
result[0][i] = s[i][0]
result[1][i] = s[i][1]

View File

@@ -19,6 +19,9 @@ when defined(windows):
elif defined(linux):
from cpuinfo import countProcessors
when defined(nimPreviewSlimSystem):
import std/syncio
type
ThreadPoolAdvice* = enum
doNothing,

View File

@@ -23,6 +23,9 @@ when not compileOption("threads"):
import cpuinfo, cpuload, locks, os
when defined(nimPreviewSlimSystem):
import std/assertions
{.push stackTrace:off.}
type

View File

@@ -11,6 +11,9 @@
import strtabs, times, options
when defined(nimPreviewSlimSystem):
import std/assertions
type
SameSite* {.pure.} = enum ## The SameSite cookie attribute.

View File

@@ -40,6 +40,8 @@ runnableExamples:
import os
when defined(nimPreviewSlimSystem):
import std/assertions
when not defined(windows):
type

View File

@@ -237,6 +237,9 @@ import std/[
asyncnet, asyncdispatch, asyncfile, nativesockets,
]
when defined(nimPreviewSlimSystem):
import std/[assertions, syncio]
export httpcore except parseHeader # TODO: The `except` doesn't work
type

View File

@@ -56,6 +56,9 @@ Please contribute a new implementation.""".}
import streams, typeinfo, json, intsets, tables, unicode
when defined(nimPreviewSlimSystem):
import std/[assertions, formatfloat]
proc ptrToInt(x: pointer): int {.inline.} =
result = cast[int](x) # don't skip alignment

View File

@@ -24,6 +24,10 @@ else:
import os, streams
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions]
proc newEIO(msg: string): ref IOError =
new(result)
result.msg = msg
@@ -313,9 +317,9 @@ when defined(posix) or defined(nimdoc):
raiseOSError(osLastError())
when defined(linux): #Maybe NetBSD, too?
#On Linux this can be over 100 times faster than a munmap,mmap cycle.
proc mremap(old: pointer; oldSize, newSize: csize; flags: cint):
proc mremap(old: pointer; oldSize, newSize: csize_t; flags: cint):
pointer {.importc: "mremap", header: "<sys/mman.h>".}
let newAddr = mremap(f.mem, csize(f.size), csize(newFileSize), cint(1))
let newAddr = mremap(f.mem, csize_t(f.size), csize_t(newFileSize), cint(1))
if newAddr == cast[pointer](MAP_FAILED):
raiseOSError(osLastError())
else:
@@ -408,7 +412,7 @@ iterator memSlices*(mfile: MemFile, delim = '\l', eat = '\r'): MemSlice {.inline
## inc(count)
## echo count
proc c_memchr(cstr: pointer, c: char, n: csize): pointer {.
proc c_memchr(cstr: pointer, c: char, n: csize_t): pointer {.
importc: "memchr", header: "<string.h>".}
proc `-!`(p, q: pointer): int {.inline.} = return cast[int](p) -% cast[int](q)
var ms: MemSlice
@@ -416,7 +420,7 @@ iterator memSlices*(mfile: MemFile, delim = '\l', eat = '\r'): MemSlice {.inline
ms.data = mfile.mem
var remaining = mfile.size
while remaining > 0:
ending = c_memchr(ms.data, delim, remaining)
ending = c_memchr(ms.data, delim, csize_t(remaining))
if ending == nil: # unterminated final slice
ms.size = remaining # Weird case..check eat?
yield ms

View File

@@ -29,6 +29,10 @@ runnableExamples:
import tables
from strutils import startsWith, toLowerAscii, strip
when defined(nimPreviewSlimSystem):
import std/assertions
type
MimeDB* = object
mimes: OrderedTable[string, string]

View File

@@ -16,6 +16,8 @@ import os, options
import std/private/since
import std/strbasics
when defined(nimPreviewSlimSystem):
import std/[assertions, syncio]
when hostOS == "solaris":
{.passl: "-lsocket -lnsl".}
@@ -167,7 +169,7 @@ when not useWinVersion:
else:
proc toInt(domain: Domain): cint =
result = toU32(ord(domain)).cint
result = cast[cint](uint32(ord(domain)))
proc toKnownDomain*(family: cint): Option[Domain] =
## Converts the platform-dependent `cint` to the Domain or none(),
@@ -373,9 +375,9 @@ when not useNimNetLite:
##
## On posix this will search through the `/etc/services` file.
when useWinVersion:
var s = winlean.getservbyport(ze(int16(port)).cint, proto)
var s = winlean.getservbyport(uint16(port).cint, proto)
else:
var s = posix.getservbyport(ze(int16(port)).cint, proto)
var s = posix.getservbyport(uint16(port).cint, proto)
if s == nil: raiseOSError(osLastError(), "Service not found.")
result.name = $s.s_name
result.aliases = cstringArrayToSeq(s.s_aliases)

View File

@@ -90,6 +90,9 @@ runnableExamples("-r:off"):
import std/private/since
when defined(nimPreviewSlimSystem):
import std/assertions
import nativesockets
import os, strutils, times, sets, options, std/monotimes
import ssl_config

View File

@@ -3121,7 +3121,7 @@ when defined(haiku):
B_FIND_PATH_IMAGE_PATH = 1000
proc find_path(codePointer: pointer, baseDirectory: cint, subPath: cstring,
pathBuffer: cstring, bufferSize: csize): int32
pathBuffer: cstring, bufferSize: csize_t): int32
{.importc, header: "<FindDirectory.h>".}
proc getApplHaiku(): string =
@@ -3500,14 +3500,21 @@ proc setLastModificationTime*(file: string, t: times.Time) {.noWeirdTarget.} =
discard h.closeHandle
if res == 0'i32: raiseOSError(osLastError(), file)
func isValidFilename*(filename: string, maxLen = 259.Positive): bool {.since: (1, 1), deprecated: "Deprecated since v1.5.1".} =
## Returns true if ``filename`` is valid for crossplatform use.
func isValidFilename*(filename: string, maxLen = 259.Positive): bool {.since: (1, 1).} =
## Returns `true` if `filename` is valid for crossplatform use.
##
## This is useful if you want to copy or save files across Windows, Linux, Mac, etc.
## You can pass full paths as argument too, but func only checks filenames.
##
## It uses `invalidFilenameChars`, `invalidFilenames` and `maxLen` to verify the specified `filename`.
##
## See also:
##
## * https://docs.microsoft.com/en-us/dotnet/api/system.io.pathtoolongexception
## * https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
## * https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
##
## .. warning:: This only checks filenames, not whole paths
## (because basically you can mount anything as a path on Linux).
runnableExamples:
assert not isValidFilename(" foo") # Leading white space
assert not isValidFilename("foo ") # Trailing white space
@@ -3518,9 +3525,6 @@ func isValidFilename*(filename: string, maxLen = 259.Positive): bool {.since: (1
assert not isValidFilename("") # Empty string
assert not isValidFilename("foo/") # Filename is empty
# https://docs.microsoft.com/en-us/dotnet/api/system.io.pathtoolongexception
# https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
result = true
let f = filename.splitFile()
if unlikely(f.name.len + f.ext.len > maxLen or f.name.len == 0 or
@@ -3529,6 +3533,7 @@ func isValidFilename*(filename: string, maxLen = 259.Positive): bool {.since: (1
for invalid in invalidFilenames:
if cmpIgnoreCase(f.name, invalid) == 0: return false
# deprecated declarations
when not defined(nimscript):
when not defined(js): # `noNimJs` doesn't work with templates, this should improve.

View File

@@ -192,6 +192,10 @@ proc parseWord(s: string, i: int, w: var string,
add(w, s[result])
inc(result)
proc initOptParser*(cmdline: seq[string], shortNoVal: set[char] = {},
longNoVal: seq[string] = @[];
allowWhitespaceAfterColon = true): OptParser
proc initOptParser*(cmdline = "", shortNoVal: set[char] = {},
longNoVal: seq[string] = @[];
allowWhitespaceAfterColon = true): OptParser =
@@ -214,28 +218,7 @@ proc initOptParser*(cmdline = "", shortNoVal: set[char] = {},
p = initOptParser("--left --debug:3 -l -r:2",
shortNoVal = {'l'}, longNoVal = @["left"])
result.pos = 0
result.idx = 0
result.inShortState = false
result.shortNoVal = shortNoVal
result.longNoVal = longNoVal
result.allowWhitespaceAfterColon = allowWhitespaceAfterColon
if cmdline != "":
result.cmds = parseCmdLine(cmdline)
else:
when declared(paramCount):
result.cmds = newSeq[string](paramCount())
for i in countup(1, paramCount()):
result.cmds[i-1] = paramStr(i)
else:
# we cannot provide this for NimRtl creation on Posix, because we can't
# access the command line arguments then!
doAssert false, "empty command line given but" &
" real command line is not accessible"
result.kind = cmdEnd
result.key = ""
result.val = ""
initOptParser(parseCmdLine(cmdline), shortNoVal, longNoVal, allowWhitespaceAfterColon)
proc initOptParser*(cmdline: seq[string], shortNoVal: set[char] = {},
longNoVal: seq[string] = @[];

View File

@@ -15,6 +15,9 @@
import strutils, lexbase
import std/private/decode_helpers
when defined(nimPreviewSlimSystem):
import std/assertions
# ------------------- scanner -------------------------------------------------
type

View File

@@ -149,6 +149,9 @@ an HTML document contains.
import
strutils, lexbase, streams, unicode
when defined(nimPreviewSlimSystem):
import std/assertions
# the parser treats ``<br />`` as ``<br></br>``
# xmlElementCloseEnd, ## ``/>``

View File

@@ -75,6 +75,9 @@ runnableExamples:
import algorithm, math
import std/private/since
when defined(nimPreviewSlimSystem):
import std/assertions
include system/inclrtl
{.push debugger: off.}

View File

@@ -22,6 +22,8 @@ runnableExamples:
doAssert r1 / r2 == -2 // 3
import math, hashes
when defined(nimPreviewSlimSystem):
import std/assertions
type Rational*[T] = object
## A rational number, consisting of a numerator `num` and a denominator `den`.

View File

@@ -20,7 +20,7 @@ include system/inclrtl
import streams
when defined(nimPreviewSlimSystem):
import std/syncio
import std/[syncio, formatfloat, assertions]
{.push debugger: off.} # the user does not want to trace a part
# of the standard library!

View File

@@ -29,6 +29,9 @@
import os, nativesockets
when defined(nimPreviewSlimSystem):
import std/assertions
const hasThreadSupport = compileOption("threads") and defined(threadsafe)
const ioselSupportedPlatform* = defined(macosx) or defined(freebsd) or

View File

@@ -88,7 +88,7 @@ when defined(haiku):
proc find_paths_etc(architecture: cstring, baseDirectory: cint,
subPath: cstring, flags: uint32,
paths: var ptr UncheckedArray[cstring],
pathCount: var csize): int32
pathCount: var csize_t): int32
{.importc, header: "<FindDirectory.h>".}
proc free(p: pointer) {.importc, header: "<stdlib.h>".}
@@ -137,7 +137,7 @@ iterator scanSSLCertificates*(useEnvVars = false): string =
else:
var
paths: ptr UncheckedArray[cstring]
size: csize
size: csize_t
let err = find_paths_etc(
nil, B_FIND_PATH_DATA_DIRECTORY, "ssl/CARootCertificates.pem",
B_FIND_PATH_EXISTING_ONLY, paths, size

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