mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-17 08:34:20 +00:00
Merge branch 'devel' into pr-remove-encode-overload
This commit is contained in:
41
changelog.md
41
changelog.md
@@ -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
124
compiler/aliasanalysis.nim
Normal 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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 & "]"
|
||||
|
||||
|
||||
@@ -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
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) =
|
||||
|
||||
@@ -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: ""
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]()
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -143,3 +143,4 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasTopDownInference")
|
||||
defineSymbol("nimHasTemplateRedefinitionPragma")
|
||||
defineSymbol("nimHasCstringCase")
|
||||
defineSymbol("nimHasAmbiguousEnumHint")
|
||||
|
||||
@@ -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)
|
||||
|
||||
534
compiler/dfa.nim
534
compiler/dfa.nim
@@ -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 Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen.
|
||||
## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf
|
||||
|
||||
import ast, intsets, lineinfos, renderer
|
||||
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
|
||||
|
||||
@@ -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 = ""
|
||||
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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}:
|
||||
|
||||
@@ -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) =
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
=======================
|
||||
|
||||
|
||||
17
doc/mm.md
17
doc/mm.md
@@ -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.
|
||||
|
||||
31
koch.nim
31
koch.nim
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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 {.
|
||||
|
||||
@@ -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].} =
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -66,6 +66,9 @@ from strutils import `%`
|
||||
import options
|
||||
from unicode import runeLenAt
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
export options
|
||||
|
||||
type
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -96,6 +96,10 @@
|
||||
##
|
||||
|
||||
import std/private/since
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[assertions, syncio]
|
||||
|
||||
import asyncdispatch, nativesockets, net, os
|
||||
|
||||
export SOBool
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
|
||||
import asyncfutures
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
import deques
|
||||
|
||||
type
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -19,6 +19,9 @@ when defined(windows):
|
||||
elif defined(linux):
|
||||
from cpuinfo import countProcessors
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/syncio
|
||||
|
||||
type
|
||||
ThreadPoolAdvice* = enum
|
||||
doNothing,
|
||||
|
||||
@@ -23,6 +23,9 @@ when not compileOption("threads"):
|
||||
|
||||
import cpuinfo, cpuload, locks, os
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
{.push stackTrace:off.}
|
||||
|
||||
type
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
|
||||
import strtabs, times, options
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
|
||||
type
|
||||
SameSite* {.pure.} = enum ## The SameSite cookie attribute.
|
||||
|
||||
@@ -40,6 +40,8 @@ runnableExamples:
|
||||
|
||||
|
||||
import os
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
when not defined(windows):
|
||||
type
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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] = @[];
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
import strutils, lexbase
|
||||
import std/private/decode_helpers
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
# ------------------- scanner -------------------------------------------------
|
||||
|
||||
type
|
||||
|
||||
@@ -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, ## ``/>``
|
||||
|
||||
@@ -75,6 +75,9 @@ runnableExamples:
|
||||
import algorithm, math
|
||||
import std/private/since
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
include system/inclrtl
|
||||
{.push debugger: off.}
|
||||
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user