mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-04 04:40:27 +00:00
Merge branch 'devel' into araq
This commit is contained in:
7
changelog.md
Normal file
7
changelog.md
Normal file
@@ -0,0 +1,7 @@
|
||||
## v0.18.0 - dd/mm/yyyy
|
||||
|
||||
### Changes affecting backwards compatibility
|
||||
|
||||
- Removed basic2d/basic3d out of the stdlib and into Nimble packages.
|
||||
These packages deprecated however, use the ``glm``, ``arraymancer``, ``neo``
|
||||
or another package.
|
||||
@@ -95,7 +95,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||
if a.kind == b.kind:
|
||||
case a.kind
|
||||
of nkSym:
|
||||
const varKinds = {skVar, skTemp, skProc}
|
||||
const varKinds = {skVar, skTemp, skProc, skFunc}
|
||||
# same symbol: aliasing:
|
||||
if a.sym.id == b.sym.id: result = arYes
|
||||
elif a.sym.kind in varKinds or b.sym.kind in varKinds:
|
||||
|
||||
@@ -221,6 +221,8 @@ type
|
||||
nkGotoState, # used for the state machine (for iterators)
|
||||
nkState, # give a label to a code section (for iterators)
|
||||
nkBreakState, # special break statement for easier code generation
|
||||
nkFuncDef # a func
|
||||
|
||||
TNodeKinds* = set[TNodeKind]
|
||||
|
||||
type
|
||||
@@ -352,7 +354,7 @@ type
|
||||
tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers
|
||||
tyFloat, tyFloat32, tyFloat64, tyFloat128,
|
||||
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64,
|
||||
tyUnused0, tyUnused1, tyUnused2,
|
||||
tyOptAsRef, tyUnused1, tyUnused2,
|
||||
tyVarargs,
|
||||
tyUnused,
|
||||
tyProxy # used as errornous type (for idetools)
|
||||
@@ -402,14 +404,8 @@ type
|
||||
# instantiation and prior to this it has the potential to
|
||||
# be any type.
|
||||
|
||||
tyFieldAccessor
|
||||
# Expressions such as Type.field (valid in contexts such
|
||||
# as the `is` operator and magics like `high` and `low`).
|
||||
# Could be lifted to a single argument proc returning the
|
||||
# field value.
|
||||
# sons[0]: type of containing object or tuple
|
||||
# sons[1]: field type
|
||||
# .n: nkDotExpr storing the field name
|
||||
tyOpt
|
||||
# Builtin optional type
|
||||
|
||||
tyVoid
|
||||
# now different from tyEmpty, hurray!
|
||||
@@ -534,6 +530,7 @@ type
|
||||
skConst, # a constant
|
||||
skResult, # special 'result' variable
|
||||
skProc, # a proc
|
||||
skFunc, # a func
|
||||
skMethod, # a method
|
||||
skIterator, # an iterator
|
||||
skConverter, # a type converter
|
||||
@@ -552,7 +549,7 @@ type
|
||||
TSymKinds* = set[TSymKind]
|
||||
|
||||
const
|
||||
routineKinds* = {skProc, skMethod, skIterator,
|
||||
routineKinds* = {skProc, skFunc, skMethod, skIterator,
|
||||
skConverter, skMacro, skTemplate}
|
||||
tfIncompleteStruct* = tfVarargs
|
||||
tfUncheckedArray* = tfVarargs
|
||||
@@ -621,7 +618,7 @@ type
|
||||
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
|
||||
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
||||
mReset,
|
||||
mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
|
||||
mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs,
|
||||
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
|
||||
mOrdinal,
|
||||
mInt, mInt8, mInt16, mInt32, mInt64,
|
||||
@@ -631,7 +628,7 @@ type
|
||||
mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mStmt, mTypeDesc,
|
||||
mVoidType, mPNimrodNode, mShared, mGuarded, mLock, mSpawn, mDeepCopy,
|
||||
mIsMainModule, mCompileDate, mCompileTime, mProcCall,
|
||||
mCpuEndian, mHostOS, mHostCPU, mAppType,
|
||||
mCpuEndian, mHostOS, mHostCPU, mBuildOS, mBuildCPU, mAppType,
|
||||
mNaN, mInf, mNegInf,
|
||||
mCompileOption, mCompileOptionArg,
|
||||
mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, mNKind,
|
||||
@@ -754,9 +751,9 @@ type
|
||||
TLocFlags* = set[TLocFlag]
|
||||
TLoc* = object
|
||||
k*: TLocKind # kind of location
|
||||
s*: TStorageLoc
|
||||
storage*: TStorageLoc
|
||||
flags*: TLocFlags # location's flags
|
||||
t*: PType # type of location
|
||||
lode*: PNode # Node where the location came from; can be faked
|
||||
r*: Rope # rope value of location (code generators)
|
||||
dup*: Rope # duplicated location for precise stack scans
|
||||
|
||||
@@ -934,7 +931,7 @@ type
|
||||
# the poor naming choices in the standard library.
|
||||
|
||||
const
|
||||
OverloadableSyms* = {skProc, skMethod, skIterator,
|
||||
OverloadableSyms* = {skProc, skFunc, skMethod, skIterator,
|
||||
skConverter, skModule, skTemplate, skMacro}
|
||||
|
||||
GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody,
|
||||
@@ -957,7 +954,7 @@ const
|
||||
tyTuple, tySequence}
|
||||
NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence,
|
||||
tyProc, tyString, tyError}
|
||||
ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType,
|
||||
ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType,
|
||||
skIterator,
|
||||
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
|
||||
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
|
||||
@@ -981,14 +978,14 @@ const
|
||||
|
||||
nkLiterals* = {nkCharLit..nkTripleStrLit}
|
||||
nkLambdaKinds* = {nkLambda, nkDo}
|
||||
declarativeDefs* = {nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef}
|
||||
declarativeDefs* = {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef}
|
||||
procDefs* = nkLambdaKinds + declarativeDefs
|
||||
|
||||
nkSymChoices* = {nkClosedSymChoice, nkOpenSymChoice}
|
||||
nkStrKinds* = {nkStrLit..nkTripleStrLit}
|
||||
|
||||
skLocalVars* = {skVar, skLet, skForVar, skParam, skResult}
|
||||
skProcKinds* = {skProc, skTemplate, skMacro, skIterator,
|
||||
skProcKinds* = {skProc, skFunc, skTemplate, skMacro, skIterator,
|
||||
skMethod, skConverter}
|
||||
|
||||
var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
|
||||
@@ -1264,11 +1261,10 @@ proc newType*(kind: TTypeKind, owner: PSym): PType =
|
||||
|
||||
proc mergeLoc(a: var TLoc, b: TLoc) =
|
||||
if a.k == low(a.k): a.k = b.k
|
||||
if a.s == low(a.s): a.s = b.s
|
||||
if a.storage == low(a.storage): a.storage = b.storage
|
||||
a.flags = a.flags + b.flags
|
||||
if a.t == nil: a.t = b.t
|
||||
if a.lode == nil: a.lode = b.lode
|
||||
if a.r == nil: a.r = b.r
|
||||
#if a.a == 0: a.a = b.a
|
||||
|
||||
proc newSons*(father: PNode, length: int) =
|
||||
if isNil(father.sons):
|
||||
|
||||
@@ -130,7 +130,7 @@ proc hashType(c: var MD5Context, t: PType) =
|
||||
c.hashSym body.sym
|
||||
for i in countup(1, sonsLen(t) - 2):
|
||||
c.hashType t.sons[i]
|
||||
of tyFromExpr, tyFieldAccessor:
|
||||
of tyFromExpr:
|
||||
c.hashTree(t.n)
|
||||
of tyArray:
|
||||
c.hashTree(t.sons[0].n)
|
||||
|
||||
@@ -56,7 +56,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.t, OnUnknown)
|
||||
initLoc(list, locCall, d.lode, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
@@ -241,7 +241,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.t, OnUnknown)
|
||||
initLoc(list, locCall, d.lode, OnUnknown)
|
||||
list.r = callPattern % [op.r, pl, pl.addComma, rawProc]
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
@@ -437,7 +437,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.t, OnUnknown)
|
||||
initLoc(list, locCall, d.lode, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
@@ -519,7 +519,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, nil, OnUnknown)
|
||||
initLoc(list, locCall, ri, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
|
||||
@@ -157,10 +157,23 @@ proc getStorageLoc(n: PNode): TStorageLoc =
|
||||
result = getStorageLoc(n.sons[0])
|
||||
else: result = OnUnknown
|
||||
|
||||
proc canMove(n: PNode): bool =
|
||||
# for now we're conservative here:
|
||||
if n.kind == nkBracket:
|
||||
# This needs to be kept consistent with 'const' seq code
|
||||
# generation!
|
||||
if not isDeepConstExpr(n) or n.len == 0:
|
||||
if skipTypes(n.typ, abstractVarRange).kind == tySequence:
|
||||
return true
|
||||
result = n.kind in nkCallKinds
|
||||
#if result:
|
||||
# echo n.info, " optimized ", n
|
||||
# result = false
|
||||
|
||||
proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
if dest.s == OnStack or not usesNativeGC():
|
||||
if dest.storage == OnStack or not usesNativeGC():
|
||||
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
|
||||
elif dest.s == OnHeap:
|
||||
elif dest.storage == OnHeap:
|
||||
# location is on heap
|
||||
# now the writer barrier is inlined for performance:
|
||||
#
|
||||
@@ -202,13 +215,13 @@ proc asgnComplexity(n: PNode): int =
|
||||
proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc =
|
||||
assert field != nil
|
||||
result.k = locField
|
||||
result.s = a.s
|
||||
result.t = t
|
||||
result.storage = a.storage
|
||||
result.lode = lodeTyp t
|
||||
result.r = rdLoc(a) & "." & field
|
||||
|
||||
proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
let newflags =
|
||||
if src.s == OnStatic:
|
||||
if src.storage == OnStatic:
|
||||
flags + {needToCopy}
|
||||
elif tfShallow in dest.t.flags:
|
||||
flags - {needToCopy}
|
||||
@@ -225,7 +238,7 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags,
|
||||
t: PNode, typ: PType) =
|
||||
if t == nil: return
|
||||
let newflags =
|
||||
if src.s == OnStatic:
|
||||
if src.storage == OnStatic:
|
||||
flags + {needToCopy}
|
||||
elif tfShallow in dest.t.flags:
|
||||
flags - {needToCopy}
|
||||
@@ -250,7 +263,7 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
# (for objects, etc.):
|
||||
if needToCopy notin flags or
|
||||
tfShallow in skipTypes(dest.t, abstractVarRange).flags:
|
||||
if dest.s == OnStack or not usesNativeGC():
|
||||
if dest.storage == OnStack or not usesNativeGC():
|
||||
useStringh(p.module)
|
||||
linefmt(p, cpsStmts,
|
||||
"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
|
||||
@@ -274,18 +287,18 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
of tyRef:
|
||||
genRefAssign(p, dest, src, flags)
|
||||
of tySequence:
|
||||
if needToCopy notin flags and src.s != OnStatic:
|
||||
if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode):
|
||||
genRefAssign(p, dest, src, flags)
|
||||
else:
|
||||
linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n",
|
||||
addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t))
|
||||
of tyString:
|
||||
if needToCopy notin flags and src.s != OnStatic:
|
||||
if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode):
|
||||
genRefAssign(p, dest, src, flags)
|
||||
else:
|
||||
if dest.s == OnStack or not usesNativeGC():
|
||||
if dest.storage == OnStack or not usesNativeGC():
|
||||
linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc)
|
||||
elif dest.s == OnHeap:
|
||||
elif dest.storage == OnHeap:
|
||||
# we use a temporary to care for the dreaded self assignment:
|
||||
var tmp: TLoc
|
||||
getTemp(p, ty, tmp)
|
||||
@@ -357,7 +370,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
|
||||
else: internalError("genAssignment: " & $ty.kind)
|
||||
|
||||
if optMemTracker in p.options and dest.s in {OnHeap, OnUnknown}:
|
||||
if optMemTracker in p.options and dest.storage in {OnHeap, OnUnknown}:
|
||||
#writeStackTrace()
|
||||
#echo p.currLineInfo, " requesting"
|
||||
linefmt(p, cpsStmts, "#memTrackerWrite((void*)$1, $2, $3, $4);$n",
|
||||
@@ -407,11 +420,11 @@ proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) =
|
||||
else:
|
||||
d = s # ``d`` is free, so fill it with ``s``
|
||||
|
||||
proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) =
|
||||
proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) =
|
||||
var a: TLoc
|
||||
if d.k != locNone:
|
||||
# need to generate an assignment here
|
||||
initLoc(a, locData, t, OnStatic)
|
||||
initLoc(a, locData, n, OnStatic)
|
||||
a.r = r
|
||||
if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
|
||||
else: genAssignment(p, d, a, {needToCopy})
|
||||
@@ -419,14 +432,14 @@ proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) =
|
||||
# we cannot call initLoc() here as that would overwrite
|
||||
# the flags field!
|
||||
d.k = locData
|
||||
d.t = t
|
||||
d.lode = n
|
||||
d.r = r
|
||||
|
||||
proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope; s=OnUnknown) =
|
||||
proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) =
|
||||
var a: TLoc
|
||||
if d.k != locNone:
|
||||
# need to generate an assignment here
|
||||
initLoc(a, locExpr, t, s)
|
||||
initLoc(a, locExpr, n, s)
|
||||
a.r = r
|
||||
if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
|
||||
else: genAssignment(p, d, a, {needToCopy})
|
||||
@@ -434,7 +447,7 @@ proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope; s=OnUnknown) =
|
||||
# we cannot call initLoc() here as that would overwrite
|
||||
# the flags field!
|
||||
d.k = locExpr
|
||||
d.t = t
|
||||
d.lode = n
|
||||
d.r = r
|
||||
|
||||
proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
|
||||
@@ -456,7 +469,7 @@ proc binaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) =
|
||||
assert(e.sons[2].typ != nil)
|
||||
initLocExpr(p, e.sons[1], a)
|
||||
initLocExpr(p, e.sons[2], b)
|
||||
putIntoDest(p, d, e.typ, ropecg(p.module, frmt, [rdLoc(a), rdLoc(b)]))
|
||||
putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a), rdLoc(b)]))
|
||||
|
||||
proc binaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
|
||||
var a, b: TLoc
|
||||
@@ -464,17 +477,17 @@ proc binaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
|
||||
assert(e.sons[2].typ != nil)
|
||||
initLocExpr(p, e.sons[1], a)
|
||||
initLocExpr(p, e.sons[2], b)
|
||||
putIntoDest(p, d, e.typ, ropecg(p.module, frmt, [a.rdCharLoc, b.rdCharLoc]))
|
||||
putIntoDest(p, d, e, ropecg(p.module, frmt, [a.rdCharLoc, b.rdCharLoc]))
|
||||
|
||||
proc unaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) =
|
||||
var a: TLoc
|
||||
initLocExpr(p, e.sons[1], a)
|
||||
putIntoDest(p, d, e.typ, ropecg(p.module, frmt, [rdLoc(a)]))
|
||||
putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a)]))
|
||||
|
||||
proc unaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
|
||||
var a: TLoc
|
||||
initLocExpr(p, e.sons[1], a)
|
||||
putIntoDest(p, d, e.typ, ropecg(p.module, frmt, [rdCharLoc(a)]))
|
||||
putIntoDest(p, d, e, ropecg(p.module, frmt, [rdCharLoc(a)]))
|
||||
|
||||
proc binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc;
|
||||
frmt: string): Rope =
|
||||
@@ -514,11 +527,11 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||
let t = e.typ.skipTypes(abstractRange)
|
||||
if optOverflowCheck notin p.options:
|
||||
let res = opr[m] % [getTypeDesc(p.module, e.typ), rdLoc(a), rdLoc(b)]
|
||||
putIntoDest(p, d, e.typ, res)
|
||||
putIntoDest(p, d, e, res)
|
||||
else:
|
||||
let res = binaryArithOverflowRaw(p, t, a, b,
|
||||
if t.kind == tyInt64: prc64[m] else: prc[m])
|
||||
putIntoDest(p, d, e.typ, "($#)($#)" % [getTypeDesc(p.module, e.typ), res])
|
||||
putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res])
|
||||
|
||||
proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||
const
|
||||
@@ -535,7 +548,7 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||
if optOverflowCheck in p.options:
|
||||
linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n",
|
||||
rdLoc(a), intLiteral(firstOrd(t)))
|
||||
putIntoDest(p, d, e.typ, opr[m] % [rdLoc(a), rope(getSize(t) * 8)])
|
||||
putIntoDest(p, d, e, opr[m] % [rdLoc(a), rope(getSize(t) * 8)])
|
||||
|
||||
proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
const
|
||||
@@ -593,7 +606,7 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
# BUGFIX: cannot use result-type here, as it may be a boolean
|
||||
s = max(getSize(a.t), getSize(b.t)) * 8
|
||||
k = getSize(a.t) * 8
|
||||
putIntoDest(p, d, e.typ,
|
||||
putIntoDest(p, d, e,
|
||||
binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s),
|
||||
getSimpleTypeDesc(p.module, e.typ), rope(k)])
|
||||
|
||||
@@ -604,10 +617,10 @@ proc genEqProc(p: BProc, e: PNode, d: var TLoc) =
|
||||
initLocExpr(p, e.sons[1], a)
|
||||
initLocExpr(p, e.sons[2], b)
|
||||
if a.t.skipTypes(abstractInst).callConv == ccClosure:
|
||||
putIntoDest(p, d, e.typ,
|
||||
putIntoDest(p, d, e,
|
||||
"($1.ClP_0 == $2.ClP_0 && $1.ClE_0 == $2.ClE_0)" % [rdLoc(a), rdLoc(b)])
|
||||
else:
|
||||
putIntoDest(p, d, e.typ, "($1 == $2)" % [rdLoc(a), rdLoc(b)])
|
||||
putIntoDest(p, d, e, "($1 == $2)" % [rdLoc(a), rdLoc(b)])
|
||||
|
||||
proc genIsNil(p: BProc, e: PNode, d: var TLoc) =
|
||||
let t = skipTypes(e.sons[1].typ, abstractRange)
|
||||
@@ -644,7 +657,7 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
assert(e.sons[1].typ != nil)
|
||||
initLocExpr(p, e.sons[1], a)
|
||||
t = skipTypes(e.typ, abstractRange)
|
||||
putIntoDest(p, d, e.typ,
|
||||
putIntoDest(p, d, e,
|
||||
unArithTab[op] % [rdLoc(a), rope(getSize(t) * 8),
|
||||
getSimpleTypeDesc(p.module, e.typ)])
|
||||
|
||||
@@ -662,7 +675,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
|
||||
# message(e.info, warnUser, "CAME HERE " & renderTree(e))
|
||||
expr(p, e.sons[0], d)
|
||||
if e.sons[0].typ.skipTypes(abstractInst).kind == tyRef:
|
||||
d.s = OnHeap
|
||||
d.storage = OnHeap
|
||||
else:
|
||||
var a: TLoc
|
||||
var typ = skipTypes(e.sons[0].typ, abstractInst)
|
||||
@@ -675,25 +688,25 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
|
||||
initLocExprSingleUse(p, e.sons[0], a)
|
||||
if d.k == locNone:
|
||||
# dest = *a; <-- We do not know that 'dest' is on the heap!
|
||||
# It is completely wrong to set 'd.s' here, unless it's not yet
|
||||
# It is completely wrong to set 'd.storage' here, unless it's not yet
|
||||
# been assigned to.
|
||||
case typ.kind
|
||||
of tyRef:
|
||||
d.s = OnHeap
|
||||
d.storage = OnHeap
|
||||
of tyVar:
|
||||
d.s = OnUnknown
|
||||
d.storage = OnUnknown
|
||||
if tfVarIsPtr notin typ.flags and p.module.compileToCpp and
|
||||
e.kind == nkHiddenDeref:
|
||||
putIntoDest(p, d, e.typ, rdLoc(a), a.s)
|
||||
putIntoDest(p, d, e, rdLoc(a), a.storage)
|
||||
return
|
||||
of tyPtr:
|
||||
d.s = OnUnknown # BUGFIX!
|
||||
d.storage = OnUnknown # BUGFIX!
|
||||
else:
|
||||
internalError(e.info, "genDeref " & $typ.kind)
|
||||
elif p.module.compileToCpp:
|
||||
if typ.kind == tyVar and tfVarIsPtr notin typ.flags and
|
||||
e.kind == nkHiddenDeref:
|
||||
putIntoDest(p, d, e.typ, rdLoc(a), a.s)
|
||||
putIntoDest(p, d, e, rdLoc(a), a.storage)
|
||||
return
|
||||
if enforceDeref and mt == ctPtrToArray:
|
||||
# we lie about the type for better C interop: 'ptr array[3,T]' is
|
||||
@@ -701,26 +714,26 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
|
||||
# See tmissingderef. So we get rid of the deref instead. The codegen
|
||||
# ends up using 'memcpy' for the array assignment,
|
||||
# so the '&' and '*' cancel out:
|
||||
putIntoDest(p, d, a.t.sons[0], rdLoc(a), a.s)
|
||||
putIntoDest(p, d, lodeTyp(a.t.sons[0]), rdLoc(a), a.storage)
|
||||
else:
|
||||
putIntoDest(p, d, e.typ, "(*$1)" % [rdLoc(a)], a.s)
|
||||
putIntoDest(p, d, e, "(*$1)" % [rdLoc(a)], a.storage)
|
||||
|
||||
proc genAddr(p: BProc, e: PNode, d: var TLoc) =
|
||||
# careful 'addr(myptrToArray)' needs to get the ampersand:
|
||||
if e.sons[0].typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
|
||||
var a: TLoc
|
||||
initLocExpr(p, e.sons[0], a)
|
||||
putIntoDest(p, d, e.typ, "&" & a.r, a.s)
|
||||
putIntoDest(p, d, e, "&" & a.r, a.storage)
|
||||
#Message(e.info, warnUser, "HERE NEW &")
|
||||
elif mapType(e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ):
|
||||
expr(p, e.sons[0], d)
|
||||
else:
|
||||
var a: TLoc
|
||||
initLocExpr(p, e.sons[0], a)
|
||||
putIntoDest(p, d, e.typ, addrLoc(a), a.s)
|
||||
putIntoDest(p, d, e, addrLoc(a), a.storage)
|
||||
|
||||
template inheritLocation(d: var TLoc, a: TLoc) =
|
||||
if d.k == locNone: d.s = a.s
|
||||
if d.k == locNone: d.storage = a.storage
|
||||
|
||||
proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc) =
|
||||
initLocExpr(p, e.sons[0], a)
|
||||
@@ -742,7 +755,7 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
|
||||
of nkIntLit..nkUInt64Lit: i = int(e.sons[1].intVal)
|
||||
else: internalError(e.info, "genTupleElem")
|
||||
addf(r, ".Field$1", [rope(i)])
|
||||
putIntoDest(p, d, tupType.sons[i], r, a.s)
|
||||
putIntoDest(p, d, e, r, a.storage)
|
||||
|
||||
proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope;
|
||||
resTyp: ptr PType = nil): PSym =
|
||||
@@ -769,15 +782,14 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
|
||||
# we found a unique tuple type which lacks field information
|
||||
# so we use Field$i
|
||||
addf(r, ".Field$1", [rope(f.position)])
|
||||
putIntoDest(p, d, f.typ, r, a.s)
|
||||
putIntoDest(p, d, e, r, a.storage)
|
||||
else:
|
||||
var rtyp: PType
|
||||
let field = lookupFieldAgain(p, ty, f, r, addr rtyp)
|
||||
if field.loc.r == nil and rtyp != nil: fillObjectFields(p.module, rtyp)
|
||||
if field.loc.r == nil: internalError(e.info, "genRecordField 3 " & typeToString(ty))
|
||||
addf(r, ".$1", [field.loc.r])
|
||||
putIntoDest(p, d, field.typ, r, a.s)
|
||||
#d.s = a.s
|
||||
putIntoDest(p, d, e, r, a.storage)
|
||||
|
||||
proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc)
|
||||
|
||||
@@ -792,11 +804,11 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym;
|
||||
if op.magic == mNot: it = it.sons[1]
|
||||
let disc = it.sons[2].skipConv
|
||||
assert(disc.kind == nkSym)
|
||||
initLoc(test, locNone, it.typ, OnStack)
|
||||
initLoc(test, locNone, it, OnStack)
|
||||
initLocExpr(p, it.sons[1], u)
|
||||
var o = obj
|
||||
let d = lookupFieldAgain(p, origTy, disc.sym, o)
|
||||
initLoc(v, locExpr, d.typ, OnUnknown)
|
||||
initLoc(v, locExpr, disc, OnUnknown)
|
||||
v.r = o
|
||||
v.r.add(".")
|
||||
v.r.add(d.loc.r)
|
||||
@@ -827,11 +839,11 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
|
||||
internalError(e.info, "genCheckedRecordField") # generate the checks:
|
||||
genFieldCheck(p, e, r, field, ty)
|
||||
add(r, rfmt(nil, ".$1", field.loc.r))
|
||||
putIntoDest(p, d, field.typ, r, a.s)
|
||||
putIntoDest(p, d, e.sons[0], r, a.storage)
|
||||
else:
|
||||
genRecordField(p, e.sons[0], d)
|
||||
|
||||
proc genArrayElem(p: BProc, x, y: PNode, d: var TLoc) =
|
||||
proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
||||
var a, b: TLoc
|
||||
initLocExpr(p, x, a)
|
||||
initLocExpr(p, y, b)
|
||||
@@ -853,30 +865,30 @@ proc genArrayElem(p: BProc, x, y: PNode, d: var TLoc) =
|
||||
if idx < firstOrd(ty) or idx > lastOrd(ty):
|
||||
localError(x.info, errIndexOutOfBounds)
|
||||
d.inheritLocation(a)
|
||||
putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)),
|
||||
rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.s)
|
||||
putIntoDest(p, d, n,
|
||||
rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.storage)
|
||||
|
||||
proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) =
|
||||
proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
||||
var a, b: TLoc
|
||||
initLocExpr(p, x, a)
|
||||
initLocExpr(p, y, b)
|
||||
var ty = skipTypes(a.t, abstractVarRange)
|
||||
if d.k == locNone: d.s = a.s
|
||||
putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)),
|
||||
rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s)
|
||||
inheritLocation(d, a)
|
||||
putIntoDest(p, d, n,
|
||||
rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage)
|
||||
|
||||
proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) =
|
||||
proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
||||
var a, b: TLoc
|
||||
initLocExpr(p, x, a)
|
||||
initLocExpr(p, y, b) # emit range check:
|
||||
if optBoundsCheck in p.options:
|
||||
linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError();$n",
|
||||
rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``!
|
||||
if d.k == locNone: d.s = a.s
|
||||
putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)),
|
||||
rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s)
|
||||
inheritLocation(d, a)
|
||||
putIntoDest(p, d, n,
|
||||
rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage)
|
||||
|
||||
proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) =
|
||||
proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
||||
var a, b: TLoc
|
||||
initLocExpr(p, x, a)
|
||||
initLocExpr(p, y, b)
|
||||
@@ -892,20 +904,20 @@ proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) =
|
||||
linefmt(p, cpsStmts,
|
||||
"if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
|
||||
rdLoc(b), rdLoc(a), lenField(p))
|
||||
if d.k == locNone: d.s = OnHeap
|
||||
if d.k == locNone: d.storage = OnHeap
|
||||
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
|
||||
a.r = rfmt(nil, "(*$1)", a.r)
|
||||
putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)),
|
||||
rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.s)
|
||||
putIntoDest(p, d, n,
|
||||
rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.storage)
|
||||
|
||||
proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
|
||||
var ty = skipTypes(n.sons[0].typ, abstractVarRange + tyUserTypeClasses)
|
||||
if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange)
|
||||
case ty.kind
|
||||
of tyArray: genArrayElem(p, n.sons[0], n.sons[1], d)
|
||||
of tyOpenArray, tyVarargs: genOpenArrayElem(p, n.sons[0], n.sons[1], d)
|
||||
of tySequence, tyString: genSeqElem(p, n.sons[0], n.sons[1], d)
|
||||
of tyCString: genCStringElem(p, n.sons[0], n.sons[1], d)
|
||||
of tyArray: genArrayElem(p, n, n.sons[0], n.sons[1], d)
|
||||
of tyOpenArray, tyVarargs: genOpenArrayElem(p, n, n.sons[0], n.sons[1], d)
|
||||
of tySequence, tyString: genSeqElem(p, n, n.sons[0], n.sons[1], d)
|
||||
of tyCString: genCStringElem(p, n, n.sons[0], n.sons[1], d)
|
||||
of tyTuple: genTupleElem(p, n, d)
|
||||
else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
|
||||
|
||||
@@ -1071,7 +1083,7 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
|
||||
getTypeDesc(p.module, bt)])
|
||||
#if bt != b.t:
|
||||
# echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
|
||||
initLoc(dest, locExpr, bt, OnHeap)
|
||||
initLoc(dest, locExpr, e.sons[2], OnHeap)
|
||||
getIntTemp(p, tmpL)
|
||||
lineCg(p, cpsStmts, "$1 = $2->$3++;$n", tmpL.r, rdLoc(a), lenField(p))
|
||||
dest.r = rfmt(nil, "$1->data[$2]", rdLoc(a), tmpL.r)
|
||||
@@ -1088,7 +1100,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
|
||||
var sizeExpr = sizeExpr
|
||||
let typ = a.t
|
||||
var b: TLoc
|
||||
initLoc(b, locExpr, a.t, OnHeap)
|
||||
initLoc(b, locExpr, a.lode, OnHeap)
|
||||
let refType = typ.skipTypes(abstractInst)
|
||||
assert refType.kind == tyRef
|
||||
let bt = refType.lastSon
|
||||
@@ -1098,7 +1110,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
|
||||
let args = [getTypeDesc(p.module, typ),
|
||||
genTypeInfo(p.module, typ),
|
||||
sizeExpr]
|
||||
if a.s == OnHeap and usesNativeGC():
|
||||
if a.storage == OnHeap and usesNativeGC():
|
||||
# use newObjRC1 as an optimization
|
||||
if canFormAcycle(a.t):
|
||||
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc)
|
||||
@@ -1128,8 +1140,8 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) =
|
||||
let args = [getTypeDesc(p.module, seqtype),
|
||||
genTypeInfo(p.module, seqtype), length]
|
||||
var call: TLoc
|
||||
initLoc(call, locExpr, dest.t, OnHeap)
|
||||
if dest.s == OnHeap and usesNativeGC():
|
||||
initLoc(call, locExpr, dest.lode, OnHeap)
|
||||
if dest.storage == OnHeap and usesNativeGC():
|
||||
if canFormAcycle(dest.t):
|
||||
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc)
|
||||
else:
|
||||
@@ -1151,7 +1163,7 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
|
||||
let seqtype = skipTypes(e.typ, abstractVarRange)
|
||||
var a: TLoc
|
||||
initLocExpr(p, e.sons[1], a)
|
||||
putIntoDest(p, d, e.typ, ropecg(p.module,
|
||||
putIntoDest(p, d, e, ropecg(p.module,
|
||||
"($1)#nimNewSeqOfCap($2, $3)", [
|
||||
getTypeDesc(p.module, seqtype),
|
||||
genTypeInfo(p.module, seqtype), a.rdLoc]))
|
||||
@@ -1163,7 +1175,7 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
|
||||
let t = n.typ
|
||||
discard getTypeDesc(p.module, t) # so that any fields are initialized
|
||||
let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
|
||||
fillLoc(d, locData, t, p.module.tmpBase & rope(id), OnStatic)
|
||||
fillLoc(d, locData, n, p.module.tmpBase & rope(id), OnStatic)
|
||||
if id == p.module.labels:
|
||||
# expression not found in the cache:
|
||||
inc(p.module.labels)
|
||||
@@ -1205,8 +1217,8 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||
add(tmp2.r, ".")
|
||||
add(tmp2.r, field.loc.r)
|
||||
tmp2.k = locTemp
|
||||
tmp2.t = field.loc.t
|
||||
tmp2.s = if isRef: OnHeap else: OnStack
|
||||
tmp2.lode = it.sons[1]
|
||||
tmp2.storage = if isRef: OnHeap else: OnStack
|
||||
expr(p, it.sons[1], tmp2)
|
||||
|
||||
if d.k == locNone:
|
||||
@@ -1214,37 +1226,37 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||
else:
|
||||
genAssignment(p, d, tmp, {})
|
||||
|
||||
proc genSeqConstr(p: BProc, t: PNode, d: var TLoc) =
|
||||
proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
|
||||
var arr: TLoc
|
||||
if d.k == locNone:
|
||||
getTemp(p, t.typ, d)
|
||||
getTemp(p, n.typ, d)
|
||||
# generate call to newSeq before adding the elements per hand:
|
||||
genNewSeqAux(p, d, intLiteral(sonsLen(t)))
|
||||
for i in countup(0, sonsLen(t) - 1):
|
||||
initLoc(arr, locExpr, elemType(skipTypes(t.typ, typedescInst)), OnHeap)
|
||||
genNewSeqAux(p, d, intLiteral(sonsLen(n)))
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
initLoc(arr, locExpr, n[i], OnHeap)
|
||||
arr.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i))
|
||||
arr.s = OnHeap # we know that sequences are on the heap
|
||||
expr(p, t.sons[i], arr)
|
||||
gcUsage(t)
|
||||
arr.storage = OnHeap # we know that sequences are on the heap
|
||||
expr(p, n[i], arr)
|
||||
gcUsage(n)
|
||||
|
||||
proc genArrToSeq(p: BProc, t: PNode, d: var TLoc) =
|
||||
proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
|
||||
var elem, a, arr: TLoc
|
||||
if t.sons[1].kind == nkBracket:
|
||||
t.sons[1].typ = t.typ
|
||||
genSeqConstr(p, t.sons[1], d)
|
||||
if n.sons[1].kind == nkBracket:
|
||||
n.sons[1].typ = n.typ
|
||||
genSeqConstr(p, n.sons[1], d)
|
||||
return
|
||||
if d.k == locNone:
|
||||
getTemp(p, t.typ, d)
|
||||
getTemp(p, n.typ, d)
|
||||
# generate call to newSeq before adding the elements per hand:
|
||||
var L = int(lengthOrd(t.sons[1].typ))
|
||||
var L = int(lengthOrd(n.sons[1].typ))
|
||||
|
||||
genNewSeqAux(p, d, intLiteral(L))
|
||||
initLocExpr(p, t.sons[1], a)
|
||||
initLocExpr(p, n.sons[1], a)
|
||||
for i in countup(0, L - 1):
|
||||
initLoc(elem, locExpr, elemType(skipTypes(t.typ, abstractInst)), OnHeap)
|
||||
initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
|
||||
elem.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i))
|
||||
elem.s = OnHeap # we know that sequences are on the heap
|
||||
initLoc(arr, locExpr, elemType(skipTypes(t.sons[1].typ, abstractInst)), a.s)
|
||||
elem.storage = OnHeap # we know that sequences are on the heap
|
||||
initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage)
|
||||
arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i))
|
||||
genAssignment(p, elem, arr, {afDestIsNil, needToCopy})
|
||||
|
||||
@@ -1256,7 +1268,7 @@ proc genNewFinalize(p: BProc, e: PNode) =
|
||||
refType = skipTypes(e.sons[1].typ, abstractVarRange)
|
||||
initLocExpr(p, e.sons[1], a)
|
||||
initLocExpr(p, e.sons[2], f)
|
||||
initLoc(b, locExpr, a.t, OnHeap)
|
||||
initLoc(b, locExpr, a.lode, OnHeap)
|
||||
ti = genTypeInfo(p.module, refType)
|
||||
addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
|
||||
b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [
|
||||
@@ -1308,7 +1320,7 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
|
||||
r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r))
|
||||
else:
|
||||
r = rfmt(p.module, "($1)", genOfHelper(p, dest, r))
|
||||
putIntoDest(p, d, getSysType(tyBool), r, a.s)
|
||||
putIntoDest(p, d, x, r, a.storage)
|
||||
|
||||
proc genOf(p: BProc, n: PNode, d: var TLoc) =
|
||||
genOf(p, n.sons[1], n.sons[2].typ, d)
|
||||
@@ -1319,52 +1331,53 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
|
||||
var t = skipTypes(e.sons[1].typ, abstractVarRange)
|
||||
case t.kind
|
||||
of tyInt..tyInt64, tyUInt..tyUInt64:
|
||||
putIntoDest(p, d, e.typ,
|
||||
ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]), a.s)
|
||||
putIntoDest(p, d, e,
|
||||
ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]), a.storage)
|
||||
of tyFloat..tyFloat128:
|
||||
putIntoDest(p, d, e.typ, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)]), a.s)
|
||||
putIntoDest(p, d, e, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)]), a.storage)
|
||||
of tyBool:
|
||||
putIntoDest(p, d, e.typ, ropecg(p.module, "#reprBool($1)", [rdLoc(a)]), a.s)
|
||||
putIntoDest(p, d, e, ropecg(p.module, "#reprBool($1)", [rdLoc(a)]), a.storage)
|
||||
of tyChar:
|
||||
putIntoDest(p, d, e.typ, ropecg(p.module, "#reprChar($1)", [rdLoc(a)]), a.s)
|
||||
putIntoDest(p, d, e, ropecg(p.module, "#reprChar($1)", [rdLoc(a)]), a.storage)
|
||||
of tyEnum, tyOrdinal:
|
||||
putIntoDest(p, d, e.typ,
|
||||
putIntoDest(p, d, e,
|
||||
ropecg(p.module, "#reprEnum((NI)$1, $2)", [
|
||||
rdLoc(a), genTypeInfo(p.module, t)]), a.s)
|
||||
rdLoc(a), genTypeInfo(p.module, t)]), a.storage)
|
||||
of tyString:
|
||||
putIntoDest(p, d, e.typ, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.s)
|
||||
putIntoDest(p, d, e, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.storage)
|
||||
of tySet:
|
||||
putIntoDest(p, d, e.typ, ropecg(p.module, "#reprSet($1, $2)", [
|
||||
addrLoc(a), genTypeInfo(p.module, t)]), a.s)
|
||||
putIntoDest(p, d, e, ropecg(p.module, "#reprSet($1, $2)", [
|
||||
addrLoc(a), genTypeInfo(p.module, t)]), a.storage)
|
||||
of tyOpenArray, tyVarargs:
|
||||
var b: TLoc
|
||||
case a.t.kind
|
||||
of tyOpenArray, tyVarargs:
|
||||
putIntoDest(p, b, e.typ, "$1, $1Len_0" % [rdLoc(a)], a.s)
|
||||
putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
|
||||
of tyString, tySequence:
|
||||
putIntoDest(p, b, e.typ,
|
||||
"$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.s)
|
||||
putIntoDest(p, b, e,
|
||||
"$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.storage)
|
||||
of tyArray:
|
||||
putIntoDest(p, b, e.typ,
|
||||
"$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.s)
|
||||
putIntoDest(p, b, e,
|
||||
"$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.storage)
|
||||
else: internalError(e.sons[0].info, "genRepr()")
|
||||
putIntoDest(p, d, e.typ,
|
||||
putIntoDest(p, d, e,
|
||||
ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b),
|
||||
genTypeInfo(p.module, elemType(t))]), a.s)
|
||||
genTypeInfo(p.module, elemType(t))]), a.storage)
|
||||
of tyCString, tyArray, tyRef, tyPtr, tyPointer, tyNil, tySequence:
|
||||
putIntoDest(p, d, e.typ,
|
||||
putIntoDest(p, d, e,
|
||||
ropecg(p.module, "#reprAny($1, $2)", [
|
||||
rdLoc(a), genTypeInfo(p.module, t)]), a.s)
|
||||
rdLoc(a), genTypeInfo(p.module, t)]), a.storage)
|
||||
of tyEmpty, tyVoid:
|
||||
localError(e.info, "'repr' doesn't support 'void' type")
|
||||
else:
|
||||
putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)",
|
||||
[addrLoc(a), genTypeInfo(p.module, t)]), a.s)
|
||||
putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)",
|
||||
[addrLoc(a), genTypeInfo(p.module, t)]),
|
||||
a.storage)
|
||||
gcUsage(e)
|
||||
|
||||
proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) =
|
||||
let t = e.sons[1].typ
|
||||
putIntoDest(p, d, e.typ, genTypeInfo(p.module, t))
|
||||
putIntoDest(p, d, e, genTypeInfo(p.module, t))
|
||||
|
||||
proc genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) =
|
||||
var a: TLoc
|
||||
@@ -1409,11 +1422,11 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
else:
|
||||
frmt = "$1 = ($2 ? $2->len : 0);$n"
|
||||
lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a))
|
||||
putIntoDest(p, d, e.typ, tmp.r)
|
||||
putIntoDest(p, d, e, tmp.r)
|
||||
of tyArray:
|
||||
# YYY: length(sideeffect) is optimized away incorrectly?
|
||||
if op == mHigh: putIntoDest(p, d, e.typ, rope(lastOrd(typ)))
|
||||
else: putIntoDest(p, d, e.typ, rope(lengthOrd(typ)))
|
||||
if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(typ)))
|
||||
else: putIntoDest(p, d, e, rope(lengthOrd(typ)))
|
||||
else: internalError(e.info, "genArrayLen()")
|
||||
|
||||
proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
|
||||
@@ -1471,7 +1484,7 @@ proc fewCmps(s: PNode): bool =
|
||||
result = sonsLen(s) <= 8 # 8 seems to be a good value
|
||||
|
||||
proc binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) =
|
||||
putIntoDest(p, d, e.typ, frmt % [rdLoc(a), rdSetElemLoc(b, a.t)])
|
||||
putIntoDest(p, d, e, frmt % [rdLoc(a), rdSetElemLoc(b, a.t)])
|
||||
|
||||
proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) =
|
||||
case int(getSize(skipTypes(e.sons[1].typ, abstractVar)))
|
||||
@@ -1500,7 +1513,7 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) =
|
||||
else:
|
||||
e.sons[2]
|
||||
initLocExpr(p, ea, a)
|
||||
initLoc(b, locExpr, e.typ, OnUnknown)
|
||||
initLoc(b, locExpr, e, OnUnknown)
|
||||
b.r = rope("(")
|
||||
var length = sonsLen(e.sons[1])
|
||||
for i in countup(0, length - 1):
|
||||
@@ -1514,7 +1527,7 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) =
|
||||
addf(b.r, "$1 == $2", [rdCharLoc(a), rdCharLoc(x)])
|
||||
if i < length - 1: add(b.r, " || ")
|
||||
add(b.r, ")")
|
||||
putIntoDest(p, d, e.typ, b.r)
|
||||
putIntoDest(p, d, e, b.r)
|
||||
else:
|
||||
assert(e.sons[1].typ != nil)
|
||||
assert(e.sons[2].typ != nil)
|
||||
@@ -1599,14 +1612,14 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
|
||||
initLocExpr(p, e.sons[1], a)
|
||||
let etyp = skipTypes(e.typ, abstractRange)
|
||||
if etyp.kind in ValueTypes and lfIndirect notin a.flags:
|
||||
putIntoDest(p, d, e.typ, "(*($1*) ($2))" %
|
||||
[getTypeDesc(p.module, e.typ), addrLoc(a)], a.s)
|
||||
putIntoDest(p, d, e, "(*($1*) ($2))" %
|
||||
[getTypeDesc(p.module, e.typ), addrLoc(a)], a.storage)
|
||||
elif etyp.kind == tyProc and etyp.callConv == ccClosure:
|
||||
putIntoDest(p, d, e.typ, "(($1) ($2))" %
|
||||
[getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.s)
|
||||
putIntoDest(p, d, e, "(($1) ($2))" %
|
||||
[getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage)
|
||||
else:
|
||||
putIntoDest(p, d, e.typ, "(($1) ($2))" %
|
||||
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.s)
|
||||
putIntoDest(p, d, e, "(($1) ($2))" %
|
||||
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
|
||||
|
||||
proc genCast(p: BProc, e: PNode, d: var TLoc) =
|
||||
const ValueTypes = {tyFloat..tyFloat128, tyTuple, tyObject, tyArray}
|
||||
@@ -1622,11 +1635,11 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) =
|
||||
linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n",
|
||||
getTypeDesc(p.module, e.sons[1].typ), getTypeDesc(p.module, e.typ), lbl)
|
||||
tmp.k = locExpr
|
||||
tmp.t = srct
|
||||
tmp.s = OnStack
|
||||
tmp.lode = lodeTyp srct
|
||||
tmp.storage = OnStack
|
||||
tmp.flags = {}
|
||||
expr(p, e.sons[1], tmp)
|
||||
putIntoDest(p, d, e.typ, "LOC$#.dest" % [lbl], tmp.s)
|
||||
putIntoDest(p, d, e, "LOC$#.dest" % [lbl], tmp.storage)
|
||||
else:
|
||||
# I prefer the shorter cast version for pointer types -> generate less
|
||||
# C code; plus it's the right thing to do for closures:
|
||||
@@ -1639,14 +1652,14 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) =
|
||||
if optRangeCheck notin p.options or dest.skipTypes({tyRange}).kind in
|
||||
{tyUInt..tyUInt64}:
|
||||
initLocExpr(p, n.sons[0], a)
|
||||
putIntoDest(p, d, n.typ, "(($1) ($2))" %
|
||||
[getTypeDesc(p.module, dest), rdCharLoc(a)], a.s)
|
||||
putIntoDest(p, d, n, "(($1) ($2))" %
|
||||
[getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)
|
||||
else:
|
||||
initLocExpr(p, n.sons[0], a)
|
||||
putIntoDest(p, d, dest, ropecg(p.module, "(($1)#$5($2, $3, $4))", [
|
||||
putIntoDest(p, d, lodeTyp dest, ropecg(p.module, "(($1)#$5($2, $3, $4))", [
|
||||
getTypeDesc(p.module, dest), rdCharLoc(a),
|
||||
genLiteral(p, n.sons[1], dest), genLiteral(p, n.sons[2], dest),
|
||||
rope(magic)]), a.s)
|
||||
rope(magic)]), a.storage)
|
||||
|
||||
proc genConv(p: BProc, e: PNode, d: var TLoc) =
|
||||
let destType = e.typ.skipTypes({tyVar, tyGenericInst, tyAlias})
|
||||
@@ -1658,13 +1671,15 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) =
|
||||
proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
|
||||
var a: TLoc
|
||||
initLocExpr(p, n.sons[0], a)
|
||||
putIntoDest(p, d, skipTypes(n.typ, abstractVar), "$1->data" % [rdLoc(a)], a.s)
|
||||
putIntoDest(p, d, n, "$1->data" % [rdLoc(a)],
|
||||
a.storage)
|
||||
|
||||
proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
|
||||
var a: TLoc
|
||||
initLocExpr(p, n.sons[0], a)
|
||||
putIntoDest(p, d, skipTypes(n.typ, abstractVar),
|
||||
ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), a.s)
|
||||
putIntoDest(p, d, n,
|
||||
ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]),
|
||||
a.storage)
|
||||
gcUsage(n)
|
||||
|
||||
proc genStrEquals(p: BProc, e: PNode, d: var TLoc) =
|
||||
@@ -1675,11 +1690,11 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) =
|
||||
binaryExpr(p, e, d, "($1 == $2)")
|
||||
elif (a.kind in {nkStrLit..nkTripleStrLit}) and (a.strVal == ""):
|
||||
initLocExpr(p, e.sons[2], x)
|
||||
putIntoDest(p, d, e.typ,
|
||||
putIntoDest(p, d, e,
|
||||
rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p)))
|
||||
elif (b.kind in {nkStrLit..nkTripleStrLit}) and (b.strVal == ""):
|
||||
initLocExpr(p, e.sons[1], x)
|
||||
putIntoDest(p, d, e.typ,
|
||||
putIntoDest(p, d, e,
|
||||
rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p)))
|
||||
else:
|
||||
binaryExpr(p, e, d, "#eqStrings($1, $2)")
|
||||
@@ -1692,9 +1707,9 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||
assert(e.sons[2].typ != nil)
|
||||
initLocExpr(p, e.sons[1], a)
|
||||
initLocExpr(p, e.sons[2], b)
|
||||
putIntoDest(p, d, e.typ, rfmt(nil, "(($4)($2) $1 ($4)($3))",
|
||||
rope(opr[m]), rdLoc(a), rdLoc(b),
|
||||
getSimpleTypeDesc(p.module, e[1].typ)))
|
||||
putIntoDest(p, d, e, rfmt(nil, "(($4)($2) $1 ($4)($3))",
|
||||
rope(opr[m]), rdLoc(a), rdLoc(b),
|
||||
getSimpleTypeDesc(p.module, e[1].typ)))
|
||||
if optNaNCheck in p.options:
|
||||
linefmt(p, cpsStmts, "#nanCheck($1);$n", rdLoc(d))
|
||||
if optInfCheck in p.options:
|
||||
@@ -1736,7 +1751,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
let ranged = skipTypes(e.sons[1].typ, {tyGenericInst, tyAlias, tyVar})
|
||||
let res = binaryArithOverflowRaw(p, ranged, a, b,
|
||||
if underlying.kind == tyInt64: fun64[op] else: fun[op])
|
||||
putIntoDest(p, a, ranged, "($#)($#)" % [
|
||||
putIntoDest(p, a, e.sons[1], "($#)($#)" % [
|
||||
getTypeDesc(p.module, ranged), res])
|
||||
|
||||
of mConStrStr: genStrConcat(p, e, d)
|
||||
@@ -1762,7 +1777,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
of mNewSeqOfCap: genNewSeqOfCap(p, e, d)
|
||||
of mSizeOf:
|
||||
let t = e.sons[1].typ.skipTypes({tyTypeDesc})
|
||||
putIntoDest(p, d, e.typ, "((NI)sizeof($1))" % [getTypeDesc(p.module, t)])
|
||||
putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t)])
|
||||
of mChr: genSomeCast(p, e, d)
|
||||
of mOrd: genOrd(p, e, d)
|
||||
of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray:
|
||||
@@ -1783,7 +1798,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
else:
|
||||
frmt = "$1 = $2->len;$n"
|
||||
lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a))
|
||||
putIntoDest(p, d, e.typ, tmp.r)
|
||||
putIntoDest(p, d, e, tmp.r)
|
||||
of mGCref: unaryStmt(p, e, d, "#nimGCref($1);$n")
|
||||
of mGCunref: unaryStmt(p, e, d, "#nimGCunref($1);$n")
|
||||
of mSetLengthStr: genSetLengthStr(p, e, d)
|
||||
@@ -1825,7 +1840,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||
var
|
||||
a, b, idx: TLoc
|
||||
if nfAllConst in e.flags:
|
||||
putIntoDest(p, d, e.typ, genSetNode(p, e))
|
||||
putIntoDest(p, d, e, genSetNode(p, e))
|
||||
else:
|
||||
if d.k == locNone: getTemp(p, e.typ, d)
|
||||
if getSize(e.typ) > 8:
|
||||
@@ -1872,7 +1887,7 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
var it = n.sons[i]
|
||||
if it.kind == nkExprColonExpr: it = it.sons[1]
|
||||
initLoc(rec, locExpr, it.typ, d.s)
|
||||
initLoc(rec, locExpr, it, d.storage)
|
||||
rec.r = "$1.Field$2" % [rdLoc(d), rope(i)]
|
||||
expr(p, it, rec)
|
||||
|
||||
@@ -1888,7 +1903,7 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) =
|
||||
var tmp = "CNSTCLOSURE" & rope(p.module.labels)
|
||||
addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n",
|
||||
[getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)])
|
||||
putIntoDest(p, d, n.typ, tmp, OnStatic)
|
||||
putIntoDest(p, d, n, tmp, OnStatic)
|
||||
else:
|
||||
var tmp, a, b: TLoc
|
||||
initLocExpr(p, n.sons[0], a)
|
||||
@@ -1911,7 +1926,7 @@ proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) =
|
||||
if not handleConstExpr(p, n, d):
|
||||
if d.k == locNone: getTemp(p, n.typ, d)
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
initLoc(arr, locExpr, elemType(skipTypes(n.typ, abstractInst)), d.s)
|
||||
initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), d.storage)
|
||||
arr.r = "$1[$2]" % [rdLoc(d), intLiteral(i)]
|
||||
expr(p, n.sons[i], arr)
|
||||
|
||||
@@ -1949,11 +1964,11 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
|
||||
linefmt(p, cpsStmts, "#chckObj($1.m_type, $2);$n",
|
||||
r, genTypeInfo(p.module, dest))
|
||||
if n.sons[0].typ.kind != tyObject:
|
||||
putIntoDest(p, d, n.typ,
|
||||
"(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.s)
|
||||
putIntoDest(p, d, n,
|
||||
"(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
|
||||
else:
|
||||
putIntoDest(p, d, n.typ, "(*($1*) ($2))" %
|
||||
[getTypeDesc(p.module, dest), addrLoc(a)], a.s)
|
||||
putIntoDest(p, d, n, "(*($1*) ($2))" %
|
||||
[getTypeDesc(p.module, dest), addrLoc(a)], a.storage)
|
||||
|
||||
proc downConv(p: BProc, n: PNode, d: var TLoc) =
|
||||
if p.module.compileToCpp:
|
||||
@@ -1985,9 +2000,9 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) =
|
||||
linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r)
|
||||
else:
|
||||
r = "&" & r
|
||||
putIntoDest(p, d, n.typ, r, a.s)
|
||||
putIntoDest(p, d, n, r, a.storage)
|
||||
else:
|
||||
putIntoDest(p, d, n.typ, r, a.s)
|
||||
putIntoDest(p, d, n, r, a.storage)
|
||||
|
||||
proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
|
||||
let t = n.typ
|
||||
@@ -2002,13 +2017,13 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
|
||||
[getTypeDesc(p.module, t), tmp, genConstExpr(p, n)])
|
||||
|
||||
if d.k == locNone:
|
||||
fillLoc(d, locData, t, tmp, OnStatic)
|
||||
fillLoc(d, locData, n, tmp, OnStatic)
|
||||
else:
|
||||
putDataIntoDest(p, d, t, tmp)
|
||||
putDataIntoDest(p, d, n, tmp)
|
||||
# This fixes bug #4551, but we really need better dataflow
|
||||
# analysis to make this 100% safe.
|
||||
if t.kind notin {tySequence, tyString}:
|
||||
d.s = OnStatic
|
||||
d.storage = OnStatic
|
||||
|
||||
proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
p.currLineInfo = n.info
|
||||
@@ -2019,31 +2034,31 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
of skMethod:
|
||||
if {sfDispatcher, sfForward} * sym.flags != {}:
|
||||
# we cannot produce code for the dispatcher yet:
|
||||
fillProcLoc(p.module, sym)
|
||||
fillProcLoc(p.module, n)
|
||||
genProcPrototype(p.module, sym)
|
||||
else:
|
||||
genProc(p.module, sym)
|
||||
putLocIntoDest(p, d, sym.loc)
|
||||
of skProc, skConverter, skIterator:
|
||||
of skProc, skConverter, skIterator, skFunc:
|
||||
#if sym.kind == skIterator:
|
||||
# echo renderTree(sym.getBody, {renderIds})
|
||||
if sfCompileTime in sym.flags:
|
||||
localError(n.info, "request to generate code for .compileTime proc: " &
|
||||
sym.name.s)
|
||||
genProc(p.module, sym)
|
||||
if sym.loc.r == nil or sym.loc.t == nil:
|
||||
if sym.loc.r == nil or sym.loc.lode == nil:
|
||||
internalError(n.info, "expr: proc not init " & sym.name.s)
|
||||
putLocIntoDest(p, d, sym.loc)
|
||||
of skConst:
|
||||
if isSimpleConst(sym.typ):
|
||||
putIntoDest(p, d, n.typ, genLiteral(p, sym.ast, sym.typ), OnStatic)
|
||||
putIntoDest(p, d, n, genLiteral(p, sym.ast, sym.typ), OnStatic)
|
||||
else:
|
||||
genComplexConst(p, sym, d)
|
||||
of skEnumField:
|
||||
putIntoDest(p, d, n.typ, rope(sym.position))
|
||||
putIntoDest(p, d, n, rope(sym.position))
|
||||
of skVar, skForVar, skResult, skLet:
|
||||
if {sfGlobal, sfThread} * sym.flags != {}:
|
||||
genVarPrototype(p.module, sym)
|
||||
genVarPrototype(p.module, n)
|
||||
if sym.loc.r == nil or sym.loc.t == nil:
|
||||
#echo "FAILED FOR PRCO ", p.prc.name.s
|
||||
#echo renderTree(p.prc.ast, {renderIds})
|
||||
@@ -2051,7 +2066,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
if sfThread in sym.flags:
|
||||
accessThreadLocalVar(p, sym)
|
||||
if emulatedThreadVars():
|
||||
putIntoDest(p, d, sym.loc.t, "NimTV_->" & sym.loc.r)
|
||||
putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.r)
|
||||
else:
|
||||
putLocIntoDest(p, d, sym.loc)
|
||||
else:
|
||||
@@ -2072,12 +2087,12 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
else: internalError(n.info, "expr(" & $sym.kind & "); unknown symbol")
|
||||
of nkNilLit:
|
||||
if not isEmptyType(n.typ):
|
||||
putIntoDest(p, d, n.typ, genLiteral(p, n))
|
||||
putIntoDest(p, d, n, genLiteral(p, n))
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
putDataIntoDest(p, d, n.typ, genLiteral(p, n))
|
||||
putDataIntoDest(p, d, n, genLiteral(p, n))
|
||||
of nkIntLit..nkUInt64Lit,
|
||||
nkFloatLit..nkFloat128Lit, nkCharLit:
|
||||
putIntoDest(p, d, n.typ, genLiteral(p, n))
|
||||
putIntoDest(p, d, n, genLiteral(p, n))
|
||||
of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand,
|
||||
nkCallStrLit:
|
||||
genLineDir(p, n)
|
||||
@@ -2097,7 +2112,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
genCall(p, n, d)
|
||||
of nkCurly:
|
||||
if isDeepConstExpr(n) and n.len != 0:
|
||||
putIntoDest(p, d, n.typ, genSetNode(p, n))
|
||||
putIntoDest(p, d, n, genSetNode(p, n))
|
||||
else:
|
||||
genSetConstr(p, n, d)
|
||||
of nkBracket:
|
||||
@@ -2138,7 +2153,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
of nkLambdaKinds:
|
||||
var sym = n.sons[namePos].sym
|
||||
genProc(p.module, sym)
|
||||
if sym.loc.r == nil or sym.loc.t == nil:
|
||||
if sym.loc.r == nil or sym.loc.lode == nil:
|
||||
internalError(n.info, "expr: proc not init " & sym.name.s)
|
||||
putLocIntoDest(p, d, sym.loc)
|
||||
of nkClosure: genClosure(p, n, d)
|
||||
@@ -2190,7 +2205,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
discard
|
||||
of nkPragma: genPragma(p, n)
|
||||
of nkPragmaBlock: expr(p, n.lastSon, d)
|
||||
of nkProcDef, nkMethodDef, nkConverterDef:
|
||||
of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef:
|
||||
if n.sons[genericParamsPos].kind == nkEmpty:
|
||||
var prc = n.sons[namePos].sym
|
||||
# due to a bug/limitation in the lambda lifting, unused inner procs
|
||||
|
||||
@@ -48,16 +48,17 @@ proc genVarTuple(p: BProc, n: PNode) =
|
||||
initLocExpr(p, n.sons[L-1], tup)
|
||||
var t = tup.t.skipTypes(abstractInst)
|
||||
for i in countup(0, L-3):
|
||||
var v = n.sons[i].sym
|
||||
let vn = n.sons[i]
|
||||
let v = vn.sym
|
||||
if sfCompileTime in v.flags: continue
|
||||
if sfGlobal in v.flags:
|
||||
assignGlobalVar(p, v)
|
||||
assignGlobalVar(p, vn)
|
||||
genObjectInit(p, cpsInit, v.typ, v.loc, true)
|
||||
registerGcRoot(p, v)
|
||||
else:
|
||||
assignLocalVar(p, v)
|
||||
assignLocalVar(p, vn)
|
||||
initLocalVar(p, v, immediateAsgn=isAssignedImmediately(n[L-1]))
|
||||
initLoc(field, locExpr, t.sons[i], tup.s)
|
||||
initLoc(field, locExpr, vn, tup.storage)
|
||||
if t.kind == tyTuple:
|
||||
field.r = "$1.Field$2" % [rdLoc(tup), rope(i)]
|
||||
else:
|
||||
@@ -171,7 +172,7 @@ proc genBreakState(p: BProc, n: PNode) =
|
||||
lineF(p, cpsStmts, "if ((((NI*) $1.ClE_0)[1]) < 0) break;$n", [rdLoc(a)])
|
||||
# lineF(p, cpsStmts, "if (($1) < 0) break;$n", [rdLoc(a)])
|
||||
|
||||
proc genVarPrototypeAux(m: BModule, sym: PSym)
|
||||
proc genVarPrototypeAux(m: BModule, n: PNode)
|
||||
|
||||
proc genGotoVar(p: BProc; value: PNode) =
|
||||
if value.kind notin {nkCharLit..nkUInt64Lit}:
|
||||
@@ -180,7 +181,8 @@ proc genGotoVar(p: BProc; value: PNode) =
|
||||
lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
|
||||
|
||||
proc genSingleVar(p: BProc, a: PNode) =
|
||||
let v = a.sons[0].sym
|
||||
let vn = a.sons[0]
|
||||
let v = vn.sym
|
||||
if sfCompileTime in v.flags: return
|
||||
if sfGoto in v.flags:
|
||||
# translate 'var state {.goto.} = X' into 'goto LX':
|
||||
@@ -195,7 +197,7 @@ proc genSingleVar(p: BProc, a: PNode) =
|
||||
if sfPure in v.flags:
|
||||
# v.owner.kind != skModule:
|
||||
targetProc = p.module.preInitProc
|
||||
assignGlobalVar(targetProc, v)
|
||||
assignGlobalVar(targetProc, vn)
|
||||
# XXX: be careful here.
|
||||
# Global variables should not be zeromem-ed within loops
|
||||
# (see bug #20).
|
||||
@@ -206,7 +208,7 @@ proc genSingleVar(p: BProc, a: PNode) =
|
||||
# Alternative construction using default constructor (which may zeromem):
|
||||
# if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc)
|
||||
if sfExportc in v.flags and p.module.g.generatedHeader != nil:
|
||||
genVarPrototypeAux(p.module.g.generatedHeader, v)
|
||||
genVarPrototypeAux(p.module.g.generatedHeader, vn)
|
||||
registerGcRoot(p, v)
|
||||
else:
|
||||
let value = a.sons[2]
|
||||
@@ -217,7 +219,7 @@ proc genSingleVar(p: BProc, a: PNode) =
|
||||
# parameterless constructor followed by an assignment operator. So we
|
||||
# generate better code here:
|
||||
genLineDir(p, a)
|
||||
let decl = localVarDecl(p, v)
|
||||
let decl = localVarDecl(p, vn)
|
||||
var tmp: TLoc
|
||||
if value.kind in nkCallKinds and value[0].kind == nkSym and
|
||||
sfConstructor in value[0].sym.flags:
|
||||
@@ -233,7 +235,7 @@ proc genSingleVar(p: BProc, a: PNode) =
|
||||
initLocExprSingleUse(p, value, tmp)
|
||||
lineF(p, cpsStmts, "$# = $#;$n", [decl, tmp.rdLoc])
|
||||
return
|
||||
assignLocalVar(p, v)
|
||||
assignLocalVar(p, vn)
|
||||
initLocalVar(p, v, imm)
|
||||
|
||||
if a.sons[2].kind != nkEmpty:
|
||||
@@ -513,7 +515,7 @@ proc genParForStmt(p: BProc, t: PNode) =
|
||||
preserveBreakIdx:
|
||||
let forLoopVar = t.sons[0].sym
|
||||
var rangeA, rangeB: TLoc
|
||||
assignLocalVar(p, forLoopVar)
|
||||
assignLocalVar(p, t.sons[0])
|
||||
#initLoc(forLoopVar.loc, locLocalVar, forLoopVar.typ, onStack)
|
||||
#discard mangleName(forLoopVar)
|
||||
let call = t.sons[1]
|
||||
@@ -958,7 +960,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
|
||||
res.add(t.sons[i].strVal)
|
||||
of nkSym:
|
||||
var sym = t.sons[i].sym
|
||||
if sym.kind in {skProc, skIterator, skMethod}:
|
||||
if sym.kind in {skProc, skFunc, skIterator, skMethod}:
|
||||
var a: TLoc
|
||||
initLocExpr(p, t.sons[i], a)
|
||||
res.add($rdLoc(a))
|
||||
|
||||
@@ -121,7 +121,8 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash;
|
||||
var c: TTraversalClosure
|
||||
var p = newProc(nil, m)
|
||||
result = "Marker_" & getTypeName(m, origTyp, sig)
|
||||
let typ = origTyp.skipTypes(abstractInst)
|
||||
var typ = origTyp.skipTypes(abstractInst)
|
||||
if typ.kind == tyOpt: typ = optLowering(typ)
|
||||
|
||||
case reason
|
||||
of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n"
|
||||
|
||||
@@ -183,7 +183,7 @@ proc mapType(typ: PType): TCTypeKind =
|
||||
of 8: result = ctInt64
|
||||
else: internalError("mapType")
|
||||
of tyRange: result = mapType(typ.sons[0])
|
||||
of tyPtr, tyVar, tyRef:
|
||||
of tyPtr, tyVar, tyRef, tyOptAsRef:
|
||||
var base = skipTypes(typ.lastSon, typedescInst)
|
||||
case base.kind
|
||||
of tyOpenArray, tyArray, tyVarargs: result = ctPtrToArray
|
||||
@@ -194,6 +194,13 @@ proc mapType(typ: PType): TCTypeKind =
|
||||
else: result = ctPtr
|
||||
of tyPointer: result = ctPtr
|
||||
of tySequence: result = ctNimSeq
|
||||
of tyOpt:
|
||||
case optKind(typ)
|
||||
of oBool: result = ctStruct
|
||||
of oNil, oPtr: result = ctPtr
|
||||
of oEnum:
|
||||
# The 'nil' value is always negative, so we always use a signed integer
|
||||
result = if getSize(typ.sons[0]) == 8: ctInt64 else: ctInt32
|
||||
of tyProc: result = if typ.callConv != ccClosure: ctProc else: ctStruct
|
||||
of tyString: result = ctNimStr
|
||||
of tyCString: result = ctCString
|
||||
@@ -282,12 +289,13 @@ proc ccgIntroducedPtr(s: PSym): bool =
|
||||
result = (getSize(pt) > platform.floatSize*2) or (optByRef in s.options)
|
||||
else: result = false
|
||||
|
||||
proc fillResult(param: PSym) =
|
||||
fillLoc(param.loc, locParam, param.typ, ~"Result",
|
||||
proc fillResult(param: PNode) =
|
||||
fillLoc(param.sym.loc, locParam, param, ~"Result",
|
||||
OnStack)
|
||||
if mapReturnType(param.typ) != ctArray and isInvalidReturnType(param.typ):
|
||||
incl(param.loc.flags, lfIndirect)
|
||||
param.loc.s = OnUnknown
|
||||
let t = param.sym.typ
|
||||
if mapReturnType(t) != ctArray and isInvalidReturnType(t):
|
||||
incl(param.sym.loc.flags, lfIndirect)
|
||||
param.sym.loc.storage = OnUnknown
|
||||
|
||||
proc typeNameOrLiteral(m: BModule; t: PType, literal: string): Rope =
|
||||
if t.sym != nil and sfImportc in t.sym.flags and t.sym.magic == mNone:
|
||||
@@ -349,7 +357,7 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
|
||||
if result != nil: return
|
||||
result = getTypePre(m, typ, sig)
|
||||
if result != nil: return
|
||||
let concrete = typ.skipTypes(abstractInst)
|
||||
let concrete = typ.skipTypes(abstractInst + {tyOpt})
|
||||
case concrete.kind
|
||||
of tySequence, tyTuple, tyObject:
|
||||
result = getTypeName(m, typ, sig)
|
||||
@@ -375,6 +383,12 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
|
||||
of tySequence:
|
||||
result = getTypeForward(m, t, hashType(t)) & "*"
|
||||
pushType(m, t)
|
||||
of tyOpt:
|
||||
if optKind(etB) == oPtr:
|
||||
result = getTypeForward(m, t, hashType(t)) & "*"
|
||||
pushType(m, t)
|
||||
else:
|
||||
result = getTypeDescAux(m, t, check)
|
||||
else:
|
||||
result = getTypeDescAux(m, t, check)
|
||||
|
||||
@@ -398,13 +412,13 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
|
||||
var param = t.n.sons[i].sym
|
||||
if isCompileTimeOnly(param.typ): continue
|
||||
if params != nil: add(params, ~", ")
|
||||
fillLoc(param.loc, locParam, param.typ, mangleParamName(m, param),
|
||||
fillLoc(param.loc, locParam, t.n.sons[i], mangleParamName(m, param),
|
||||
param.paramStorageLoc)
|
||||
if ccgIntroducedPtr(param):
|
||||
add(params, getTypeDescWeak(m, param.typ, check))
|
||||
add(params, ~"*")
|
||||
incl(param.loc.flags, lfIndirect)
|
||||
param.loc.s = OnUnknown
|
||||
param.loc.storage = OnUnknown
|
||||
elif weakDep:
|
||||
add(params, getTypeDescWeak(m, param.typ, check))
|
||||
else:
|
||||
@@ -417,7 +431,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
|
||||
var j = 0
|
||||
while arr.kind in {tyOpenArray, tyVarargs}:
|
||||
# this fixes the 'sort' bug:
|
||||
if param.typ.kind == tyVar: param.loc.s = OnUnknown
|
||||
if param.typ.kind == tyVar: param.loc.storage = OnUnknown
|
||||
# need to pass hidden parameter:
|
||||
addf(params, ", NI $1Len_$2", [param.loc.r, j.rope])
|
||||
inc(j)
|
||||
@@ -496,16 +510,16 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
|
||||
let sname = mangleRecFieldName(m, field, rectype)
|
||||
let ae = if accessExpr != nil: "$1.$2" % [accessExpr, sname]
|
||||
else: sname
|
||||
fillLoc(field.loc, locField, field.typ, ae, OnUnknown)
|
||||
fillLoc(field.loc, locField, n, ae, OnUnknown)
|
||||
# for importcpp'ed objects, we only need to set field.loc, but don't
|
||||
# have to recurse via 'getTypeDescAux'. And not doing so prevents problems
|
||||
# with heavily templatized C++ code:
|
||||
if not isImportedCppType(rectype):
|
||||
let fieldType = field.loc.t.skipTypes(abstractInst)
|
||||
let fieldType = field.loc.lode.typ.skipTypes(abstractInst)
|
||||
if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags:
|
||||
addf(result, "$1 $2[SEQ_DECL_SIZE];$n",
|
||||
[getTypeDescAux(m, fieldType.elemType, check), sname])
|
||||
elif fieldType.kind == tySequence:
|
||||
elif fieldType.kind in {tySequence, tyOpt}:
|
||||
# we need to use a weak dependency here for trecursive_table.
|
||||
addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname])
|
||||
elif field.bitsize != 0:
|
||||
@@ -624,7 +638,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
|
||||
excl(check, t.id)
|
||||
return
|
||||
case t.kind
|
||||
of tyRef, tyPtr, tyVar:
|
||||
of tyRef, tyOptAsRef, tyPtr, tyVar:
|
||||
var star = if t.kind == tyVar and tfVarIsPtr notin origTyp.flags and
|
||||
compileToCpp(m): "&" else: "*"
|
||||
var et = origTyp.skipTypes(abstractInst).lastSon
|
||||
@@ -651,6 +665,21 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
|
||||
result = name & "*" & star
|
||||
m.typeCache[sig] = result
|
||||
pushType(m, et)
|
||||
of tyOpt:
|
||||
if etB.sons[0].kind in {tyObject, tyTuple}:
|
||||
let name = getTypeForward(m, et, hashType et)
|
||||
result = name & "*" & star
|
||||
m.typeCache[sig] = result
|
||||
pushType(m, et)
|
||||
elif optKind(etB) == oBool:
|
||||
let name = getTypeForward(m, et, hashType et)
|
||||
result = name & "*"
|
||||
m.typeCache[sig] = result
|
||||
pushType(m, et)
|
||||
else:
|
||||
# else we have a strong dependency :-(
|
||||
result = getTypeDescAux(m, et, check) & star
|
||||
m.typeCache[sig] = result
|
||||
else:
|
||||
# else we have a strong dependency :-(
|
||||
result = getTypeDescAux(m, et, check) & star
|
||||
@@ -726,6 +755,38 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
|
||||
else:
|
||||
result = rope("TGenericSeq")
|
||||
add(result, "*")
|
||||
of tyOpt:
|
||||
result = cacheGetType(m.typeCache, sig)
|
||||
if result == nil:
|
||||
case optKind(t)
|
||||
of oBool:
|
||||
result = cacheGetType(m.forwTypeCache, sig)
|
||||
if result == nil:
|
||||
result = getTypeName(m, origTyp, sig)
|
||||
addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
|
||||
[structOrUnion(t), result])
|
||||
m.forwTypeCache[sig] = result
|
||||
appcg(m, m.s[cfsSeqTypes], "struct $2 {$n" &
|
||||
" NIM_BOOL Field0;$n" &
|
||||
" $1 Field1;$n" &
|
||||
"};$n", [getTypeDescAux(m, t.sons[0], check), result])
|
||||
of oPtr:
|
||||
let et = t.sons[0]
|
||||
if et.kind in {tyTuple, tyObject}:
|
||||
let name = getTypeForward(m, et, hashType et)
|
||||
result = name & "*"
|
||||
pushType(m, et)
|
||||
else:
|
||||
result = getTypeDescAux(m, t.sons[0], check) & "*"
|
||||
of oNil:
|
||||
result = getTypeDescAux(m, t.sons[0], check)
|
||||
of oEnum:
|
||||
result = getTypeName(m, origTyp, sig)
|
||||
if getSize(t.sons[0]) == 8:
|
||||
addf(m.s[cfsTypes], "typedef NI64 $1;$n", [result])
|
||||
else:
|
||||
addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result])
|
||||
m.typeCache[sig] = result
|
||||
of tyArray:
|
||||
var n: BiggestInt = lengthOrd(t)
|
||||
if n <= 0: n = 1 # make an array of at least one element
|
||||
@@ -861,7 +922,7 @@ proc genProcHeader(m: BModule, prc: PSym): Rope =
|
||||
elif prc.typ.callConv == ccInline:
|
||||
result.add "static "
|
||||
var check = initIntSet()
|
||||
fillLoc(prc.loc, locProc, prc.typ, mangleName(m, prc), OnUnknown)
|
||||
fillLoc(prc.loc, locProc, prc.ast[namePos], mangleName(m, prc), OnUnknown)
|
||||
genProcParams(m, prc.typ, rettype, params, check)
|
||||
# careful here! don't access ``prc.ast`` as that could reload large parts of
|
||||
# the object graph!
|
||||
@@ -1113,6 +1174,8 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
|
||||
proc genTypeInfo(m: BModule, t: PType): Rope =
|
||||
let origType = t
|
||||
var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses)
|
||||
if t.kind == tyOpt:
|
||||
return genTypeInfo(m, optLowering(t))
|
||||
|
||||
let sig = hashType(origType)
|
||||
result = m.typeInfoMarker.getOrDefault(sig)
|
||||
@@ -1158,7 +1221,7 @@ proc genTypeInfo(m: BModule, t: PType): Rope =
|
||||
else:
|
||||
let x = fakeClosureType(t.owner)
|
||||
genTupleInfo(m, x, x, result)
|
||||
of tySequence, tyRef:
|
||||
of tySequence, tyRef, tyOptAsRef:
|
||||
genTypeInfoAux(m, t, t, result)
|
||||
if gSelectedGC >= gcMarkAndSweep:
|
||||
let markerProc = genTraverseProc(m, origType, sig, tiNew)
|
||||
|
||||
@@ -100,7 +100,7 @@ proc getUniqueType*(key: PType): PType =
|
||||
if result == nil:
|
||||
gCanonicalTypes[k] = key
|
||||
result = key
|
||||
of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor:
|
||||
of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr:
|
||||
if key.isResolvedUserTypeClass:
|
||||
return getUniqueType(lastSon(key))
|
||||
if key.sym != nil:
|
||||
@@ -126,7 +126,7 @@ proc getUniqueType*(key: PType): PType =
|
||||
result = slowSearch(key, k)
|
||||
of tyGenericInvocation, tyGenericBody,
|
||||
tyOpenArray, tyArray, tySet, tyRange, tyTuple,
|
||||
tySequence, tyForward, tyVarargs, tyProxy:
|
||||
tySequence, tyForward, tyVarargs, tyProxy, tyOpt:
|
||||
# we have to do a slow linear search because types may need
|
||||
# to be compared by their structure:
|
||||
result = slowSearch(key, k)
|
||||
@@ -157,7 +157,7 @@ proc getUniqueType*(key: PType): PType =
|
||||
else:
|
||||
# ugh, we need the canon here:
|
||||
result = slowSearch(key, k)
|
||||
of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("getUniqueType")
|
||||
of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("getUniqueType")
|
||||
|
||||
proc makeSingleLineCString*(s: string): string =
|
||||
result = "\""
|
||||
|
||||
@@ -47,21 +47,31 @@ proc findPendingModule(m: BModule, s: PSym): BModule =
|
||||
var ms = getModule(s)
|
||||
result = m.g.modules[ms.position]
|
||||
|
||||
proc initLoc(result: var TLoc, k: TLocKind, typ: PType, s: TStorageLoc) =
|
||||
proc initLoc(result: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) =
|
||||
result.k = k
|
||||
result.s = s
|
||||
result.t = typ
|
||||
result.storage = s
|
||||
result.lode = lode
|
||||
result.r = nil
|
||||
result.flags = {}
|
||||
|
||||
proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: Rope, s: TStorageLoc) =
|
||||
proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) =
|
||||
# fills the loc if it is not already initialized
|
||||
if a.k == locNone:
|
||||
a.k = k
|
||||
a.t = typ
|
||||
a.s = s
|
||||
a.lode = lode
|
||||
a.storage = s
|
||||
if a.r == nil: a.r = r
|
||||
|
||||
proc t(a: TLoc): PType {.inline.} =
|
||||
if a.lode.kind == nkSym:
|
||||
result = a.lode.sym.typ
|
||||
else:
|
||||
result = a.lode.typ
|
||||
|
||||
proc lodeTyp(t: PType): PNode =
|
||||
result = newNode(nkEmpty)
|
||||
result.typ = t
|
||||
|
||||
proc isSimpleConst(typ: PType): bool =
|
||||
let t = skipTypes(typ, abstractVar)
|
||||
result = t.kind notin
|
||||
@@ -286,7 +296,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
|
||||
if not isComplexValueType(typ):
|
||||
if containsGcRef:
|
||||
var nilLoc: TLoc
|
||||
initLoc(nilLoc, locTemp, loc.t, OnStack)
|
||||
initLoc(nilLoc, locTemp, loc.lode, OnStack)
|
||||
nilLoc.r = rope("NIM_NIL")
|
||||
genRefAssign(p, loc, nilLoc, {afSrcIsNil})
|
||||
else:
|
||||
@@ -294,7 +304,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
|
||||
else:
|
||||
if optNilCheck in p.options:
|
||||
linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(loc))
|
||||
if loc.s != OnStack:
|
||||
if loc.storage != OnStack:
|
||||
linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
|
||||
addrLoc(loc), genTypeInfo(p.module, loc.t))
|
||||
# XXX: generated reset procs should not touch the m_type
|
||||
@@ -340,8 +350,8 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
|
||||
result.r = "T" & rope(p.labels) & "_"
|
||||
linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r)
|
||||
result.k = locTemp
|
||||
result.t = t
|
||||
result.s = OnStack
|
||||
result.lode = lodeTyp t
|
||||
result.storage = OnStack
|
||||
result.flags = {}
|
||||
constructLoc(p, result, not needsInit)
|
||||
|
||||
@@ -350,8 +360,8 @@ proc getIntTemp(p: BProc, result: var TLoc) =
|
||||
result.r = "T" & rope(p.labels) & "_"
|
||||
linefmt(p, cpsLocals, "NI $1;$n", result.r)
|
||||
result.k = locTemp
|
||||
result.s = OnStack
|
||||
result.t = getSysType(tyInt)
|
||||
result.storage = OnStack
|
||||
result.lode = lodeTyp getSysType(tyInt)
|
||||
result.flags = {}
|
||||
|
||||
proc initGCFrame(p: BProc): Rope =
|
||||
@@ -375,9 +385,10 @@ proc localDebugInfo(p: BProc, s: PSym) =
|
||||
inc(p.maxFrameLen)
|
||||
inc p.blocks[p.blocks.len-1].frameLen
|
||||
|
||||
proc localVarDecl(p: BProc; s: PSym): Rope =
|
||||
proc localVarDecl(p: BProc; n: PNode): Rope =
|
||||
let s = n.sym
|
||||
if s.loc.k == locNone:
|
||||
fillLoc(s.loc, locLocalVar, s.typ, mangleLocalName(p, s), OnStack)
|
||||
fillLoc(s.loc, locLocalVar, n, mangleLocalName(p, s), OnStack)
|
||||
if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
|
||||
result = getTypeDesc(p.module, s.typ)
|
||||
if s.constraint.isNil:
|
||||
@@ -390,23 +401,24 @@ proc localVarDecl(p: BProc; s: PSym): Rope =
|
||||
else:
|
||||
result = s.cgDeclFrmt % [result, s.loc.r]
|
||||
|
||||
proc assignLocalVar(p: BProc, s: PSym) =
|
||||
proc assignLocalVar(p: BProc, n: PNode) =
|
||||
#assert(s.loc.k == locNone) # not yet assigned
|
||||
# this need not be fulfilled for inline procs; they are regenerated
|
||||
# for each module that uses them!
|
||||
let nl = if optLineDir in gOptions: "" else: tnl
|
||||
let decl = localVarDecl(p, s) & ";" & nl
|
||||
let decl = localVarDecl(p, n) & ";" & nl
|
||||
line(p, cpsLocals, decl)
|
||||
localDebugInfo(p, s)
|
||||
localDebugInfo(p, n.sym)
|
||||
|
||||
include ccgthreadvars
|
||||
|
||||
proc varInDynamicLib(m: BModule, sym: PSym)
|
||||
proc mangleDynLibProc(sym: PSym): Rope
|
||||
|
||||
proc assignGlobalVar(p: BProc, s: PSym) =
|
||||
proc assignGlobalVar(p: BProc, n: PNode) =
|
||||
let s = n.sym
|
||||
if s.loc.k == locNone:
|
||||
fillLoc(s.loc, locGlobalVar, s.typ, mangleName(p.module, s), OnHeap)
|
||||
fillLoc(s.loc, locGlobalVar, n, mangleName(p.module, s), OnHeap)
|
||||
|
||||
if lfDynamicLib in s.loc.flags:
|
||||
var q = findPendingModule(p.module, s)
|
||||
@@ -446,9 +458,10 @@ proc assignParam(p: BProc, s: PSym) =
|
||||
scopeMangledParam(p, s)
|
||||
localDebugInfo(p, s)
|
||||
|
||||
proc fillProcLoc(m: BModule; sym: PSym) =
|
||||
proc fillProcLoc(m: BModule; n: PNode) =
|
||||
let sym = n.sym
|
||||
if sym.loc.k == locNone:
|
||||
fillLoc(sym.loc, locProc, sym.typ, mangleName(m, sym), OnStack)
|
||||
fillLoc(sym.loc, locProc, n, mangleName(m, sym), OnStack)
|
||||
|
||||
proc getLabel(p: BProc): TLabel =
|
||||
inc(p.labels)
|
||||
@@ -457,7 +470,7 @@ proc getLabel(p: BProc): TLabel =
|
||||
proc fixLabel(p: BProc, labl: TLabel) =
|
||||
lineF(p, cpsStmts, "$1: ;$n", [labl])
|
||||
|
||||
proc genVarPrototype(m: BModule, sym: PSym)
|
||||
proc genVarPrototype(m: BModule, n: PNode)
|
||||
proc requestConstImpl(p: BProc, sym: PSym)
|
||||
proc genStmts(p: BProc, t: PNode)
|
||||
proc expr(p: BProc, n: PNode, d: var TLoc)
|
||||
@@ -469,11 +482,11 @@ proc genLiteral(p: BProc, n: PNode): Rope
|
||||
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope
|
||||
|
||||
proc initLocExpr(p: BProc, e: PNode, result: var TLoc) =
|
||||
initLoc(result, locNone, e.typ, OnUnknown)
|
||||
initLoc(result, locNone, e, OnUnknown)
|
||||
expr(p, e, result)
|
||||
|
||||
proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) =
|
||||
initLoc(result, locNone, e.typ, OnUnknown)
|
||||
initLoc(result, locNone, e, OnUnknown)
|
||||
result.flags.incl lfSingleUse
|
||||
expr(p, e, result)
|
||||
|
||||
@@ -590,8 +603,8 @@ proc cgsym(m: BModule, name: string): Rope =
|
||||
var sym = magicsys.getCompilerProc(name)
|
||||
if sym != nil:
|
||||
case sym.kind
|
||||
of skProc, skMethod, skConverter, skIterator: genProc(m, sym)
|
||||
of skVar, skResult, skLet: genVarPrototype(m, sym)
|
||||
of skProc, skFunc, skMethod, skConverter, skIterator: genProc(m, sym)
|
||||
of skVar, skResult, skLet: genVarPrototype(m, newSymNode sym)
|
||||
of skType: discard getTypeDesc(m, sym.typ)
|
||||
else: internalError("cgsym: " & name & ": " & $sym.kind)
|
||||
else:
|
||||
@@ -645,7 +658,7 @@ proc closureSetup(p: BProc, prc: PSym) =
|
||||
internalError(prc.info, "closure generation failed")
|
||||
var env = ls.sym
|
||||
#echo "created environment: ", env.id, " for ", prc.name.s
|
||||
assignLocalVar(p, env)
|
||||
assignLocalVar(p, ls)
|
||||
# generate cast assignment:
|
||||
linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n",
|
||||
rdLoc(env.loc), getTypeDesc(p.module, env.typ))
|
||||
@@ -676,29 +689,30 @@ proc genProcAux(m: BModule, prc: PSym) =
|
||||
if sfPure notin prc.flags and prc.typ.sons[0] != nil:
|
||||
if resultPos >= prc.ast.len:
|
||||
internalError(prc.info, "proc has no result symbol")
|
||||
var res = prc.ast.sons[resultPos].sym # get result symbol
|
||||
let resNode = prc.ast.sons[resultPos]
|
||||
let res = resNode.sym # get result symbol
|
||||
if not isInvalidReturnType(prc.typ.sons[0]):
|
||||
if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
|
||||
if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(prc.getBody); val != nil):
|
||||
var decl = localVarDecl(p, res)
|
||||
var decl = localVarDecl(p, resNode)
|
||||
var a: TLoc
|
||||
initLocExprSingleUse(p, val, a)
|
||||
linefmt(p, cpsStmts, "$1 = $2;$n", decl, rdLoc(a))
|
||||
else:
|
||||
# declare the result symbol:
|
||||
assignLocalVar(p, res)
|
||||
assignLocalVar(p, resNode)
|
||||
assert(res.loc.r != nil)
|
||||
initLocalVar(p, res, immediateAsgn=false)
|
||||
returnStmt = rfmt(nil, "\treturn $1;$n", rdLoc(res.loc))
|
||||
else:
|
||||
fillResult(res)
|
||||
fillResult(resNode)
|
||||
assignParam(p, res)
|
||||
if skipTypes(res.typ, abstractInst).kind == tyArray:
|
||||
#incl(res.loc.flags, lfIndirect)
|
||||
res.loc.s = OnUnknown
|
||||
res.loc.storage = OnUnknown
|
||||
|
||||
for i in countup(1, sonsLen(prc.typ.n) - 1):
|
||||
var param = prc.typ.n.sons[i].sym
|
||||
let param = prc.typ.n.sons[i].sym
|
||||
if param.typ.isCompileTimeOnly: continue
|
||||
assignParam(p, param)
|
||||
closureSetup(p, prc)
|
||||
@@ -764,13 +778,13 @@ proc genProcPrototype(m: BModule, sym: PSym) =
|
||||
|
||||
proc genProcNoForward(m: BModule, prc: PSym) =
|
||||
if lfImportCompilerProc in prc.loc.flags:
|
||||
fillProcLoc(m, prc)
|
||||
fillProcLoc(m, prc.ast[namePos])
|
||||
useHeader(m, prc)
|
||||
# dependency to a compilerproc:
|
||||
discard cgsym(m, prc.name.s)
|
||||
return
|
||||
if lfNoDecl in prc.loc.flags:
|
||||
fillProcLoc(m, prc)
|
||||
fillProcLoc(m, prc.ast[namePos])
|
||||
useHeader(m, prc)
|
||||
genProcPrototype(m, prc)
|
||||
elif prc.typ.callConv == ccInline:
|
||||
@@ -779,7 +793,7 @@ proc genProcNoForward(m: BModule, prc: PSym) =
|
||||
# a check for ``m.declaredThings``.
|
||||
if not containsOrIncl(m.declaredThings, prc.id):
|
||||
#if prc.loc.k == locNone:
|
||||
fillProcLoc(m, prc)
|
||||
fillProcLoc(m, prc.ast[namePos])
|
||||
#elif {sfExportc, sfImportc} * prc.flags == {}:
|
||||
# # reset name to restore consistency in case of hashing collisions:
|
||||
# echo "resetting ", prc.id, " by ", m.module.name.s
|
||||
@@ -790,7 +804,7 @@ proc genProcNoForward(m: BModule, prc: PSym) =
|
||||
genProcAux(m, prc)
|
||||
elif lfDynamicLib in prc.loc.flags:
|
||||
var q = findPendingModule(m, prc)
|
||||
fillProcLoc(q, prc)
|
||||
fillProcLoc(q, prc.ast[namePos])
|
||||
useHeader(m, prc)
|
||||
genProcPrototype(m, prc)
|
||||
if q != nil and not containsOrIncl(q.declaredThings, prc.id):
|
||||
@@ -799,13 +813,13 @@ proc genProcNoForward(m: BModule, prc: PSym) =
|
||||
symInDynamicLibPartial(m, prc)
|
||||
elif sfImportc notin prc.flags:
|
||||
var q = findPendingModule(m, prc)
|
||||
fillProcLoc(q, prc)
|
||||
fillProcLoc(q, prc.ast[namePos])
|
||||
useHeader(m, prc)
|
||||
genProcPrototype(m, prc)
|
||||
if q != nil and not containsOrIncl(q.declaredThings, prc.id):
|
||||
genProcAux(q, prc)
|
||||
else:
|
||||
fillProcLoc(m, prc)
|
||||
fillProcLoc(m, prc.ast[namePos])
|
||||
useHeader(m, prc)
|
||||
if sfInfixCall notin prc.flags: genProcPrototype(m, prc)
|
||||
|
||||
@@ -813,7 +827,7 @@ proc requestConstImpl(p: BProc, sym: PSym) =
|
||||
var m = p.module
|
||||
useHeader(m, sym)
|
||||
if sym.loc.k == locNone:
|
||||
fillLoc(sym.loc, locData, sym.typ, mangleName(p.module, sym), OnStatic)
|
||||
fillLoc(sym.loc, locData, sym.ast, mangleName(p.module, sym), OnStatic)
|
||||
if lfNoDecl in sym.loc.flags: return
|
||||
# declare implementation:
|
||||
var q = findPendingModule(m, sym)
|
||||
@@ -836,7 +850,7 @@ proc genProc(m: BModule, prc: PSym) =
|
||||
if sfBorrow in prc.flags or not isActivated(prc): return
|
||||
if sfForward in prc.flags:
|
||||
addForwardedProc(m, prc)
|
||||
fillProcLoc(m, prc)
|
||||
fillProcLoc(m, prc.ast[namePos])
|
||||
else:
|
||||
genProcNoForward(m, prc)
|
||||
if {sfExportc, sfCompilerProc} * prc.flags == {sfExportc} and
|
||||
@@ -846,10 +860,11 @@ proc genProc(m: BModule, prc: PSym) =
|
||||
if not containsOrIncl(m.g.generatedHeader.declaredThings, prc.id):
|
||||
genProcAux(m.g.generatedHeader, prc)
|
||||
|
||||
proc genVarPrototypeAux(m: BModule, sym: PSym) =
|
||||
proc genVarPrototypeAux(m: BModule, n: PNode) =
|
||||
#assert(sfGlobal in sym.flags)
|
||||
let sym = n.sym
|
||||
useHeader(m, sym)
|
||||
fillLoc(sym.loc, locGlobalVar, sym.typ, mangleName(m, sym), OnHeap)
|
||||
fillLoc(sym.loc, locGlobalVar, n, mangleName(m, sym), OnHeap)
|
||||
if (lfNoDecl in sym.loc.flags) or containsOrIncl(m.declaredThings, sym.id):
|
||||
return
|
||||
if sym.owner.id != m.module.id:
|
||||
@@ -865,8 +880,8 @@ proc genVarPrototypeAux(m: BModule, sym: PSym) =
|
||||
if sfVolatile in sym.flags: add(m.s[cfsVars], " volatile")
|
||||
addf(m.s[cfsVars], " $1;$n", [sym.loc.r])
|
||||
|
||||
proc genVarPrototype(m: BModule, sym: PSym) =
|
||||
genVarPrototypeAux(m, sym)
|
||||
proc genVarPrototype(m: BModule, n: PNode) =
|
||||
genVarPrototypeAux(m, n)
|
||||
|
||||
proc addIntTypes(result: var Rope) {.inline.} =
|
||||
addf(result, "#define NIM_NEW_MANGLING_RULES" & tnl &
|
||||
|
||||
@@ -107,3 +107,4 @@ proc initDefines*() =
|
||||
defineSymbol("nimDistros")
|
||||
defineSymbol("nimHasCppDefine")
|
||||
defineSymbol("nimGenericInOutFlags")
|
||||
when false: defineSymbol("nimHasOpt")
|
||||
|
||||
@@ -325,7 +325,7 @@ proc complexName(k: TSymKind, n: PNode, baseName: string): string =
|
||||
## section of ``doc/docgen.txt``.
|
||||
result = baseName
|
||||
case k:
|
||||
of skProc: result.add(defaultParamSeparator)
|
||||
of skProc, skFunc: result.add(defaultParamSeparator)
|
||||
of skMacro: result.add(".m" & defaultParamSeparator)
|
||||
of skMethod: result.add(".e" & defaultParamSeparator)
|
||||
of skIterator: result.add(".i" & defaultParamSeparator)
|
||||
@@ -341,7 +341,7 @@ proc isCallable(n: PNode): bool =
|
||||
## Returns true if `n` contains a callable node.
|
||||
case n.kind
|
||||
of nkProcDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef,
|
||||
nkConverterDef: result = true
|
||||
nkConverterDef, nkFuncDef: result = true
|
||||
else:
|
||||
result = false
|
||||
|
||||
@@ -533,6 +533,9 @@ proc generateDoc*(d: PDoc, n: PNode) =
|
||||
of nkProcDef:
|
||||
when useEffectSystem: documentRaises(n)
|
||||
genItem(d, n, n.sons[namePos], skProc)
|
||||
of nkFuncDef:
|
||||
when useEffectSystem: documentRaises(n)
|
||||
genItem(d, n, n.sons[namePos], skFunc)
|
||||
of nkMethodDef:
|
||||
when useEffectSystem: documentRaises(n)
|
||||
genItem(d, n, n.sons[namePos], skMethod)
|
||||
@@ -574,6 +577,9 @@ proc generateJson*(d: PDoc, n: PNode) =
|
||||
of nkProcDef:
|
||||
when useEffectSystem: documentRaises(n)
|
||||
d.add genJsonItem(d, n, n.sons[namePos], skProc)
|
||||
of nkFuncDef:
|
||||
when useEffectSystem: documentRaises(n)
|
||||
d.add genJsonItem(d, n, n.sons[namePos], skFunc)
|
||||
of nkMethodDef:
|
||||
when useEffectSystem: documentRaises(n)
|
||||
d.add genJsonItem(d, n, n.sons[namePos], skMethod)
|
||||
@@ -604,8 +610,8 @@ proc generateJson*(d: PDoc, n: PNode) =
|
||||
|
||||
proc genSection(d: PDoc, kind: TSymKind) =
|
||||
const sectionNames: array[skModule..skTemplate, string] = [
|
||||
"Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods",
|
||||
"Iterators", "Converters", "Macros", "Templates"
|
||||
"Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Funcs",
|
||||
"Methods", "Iterators", "Converters", "Macros", "Templates"
|
||||
]
|
||||
if d.section[kind] == nil: return
|
||||
var title = sectionNames[kind].rope
|
||||
|
||||
@@ -652,9 +652,10 @@ proc getLinkCmd(projectfile, objfiles: string): string =
|
||||
else:
|
||||
var linkerExe = getConfigVar(cCompiler, ".linkerexe")
|
||||
if len(linkerExe) == 0: linkerExe = cCompiler.getLinkerExe
|
||||
# bug #6452: We must not use ``quoteShell`` here for ``linkerExe``
|
||||
if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe")
|
||||
if noAbsolutePaths(): result = quoteShell(linkerExe)
|
||||
else: result = quoteShell(joinPath(ccompilerpath, linkerExe))
|
||||
if noAbsolutePaths(): result = linkerExe
|
||||
else: result = joinPath(ccompilerpath, linkerExe)
|
||||
let buildgui = if optGenGuiApp in gGlobalOptions: CC[cCompiler].buildGui
|
||||
else: ""
|
||||
var exefile, builddll: string
|
||||
@@ -701,6 +702,40 @@ template tryExceptOSErrorMessage(errorPrefix: string = "", body: untyped): typed
|
||||
rawMessage(errExecutionOfProgramFailed, ose.msg & " " & $ose.errorCode)
|
||||
raise
|
||||
|
||||
proc execLinkCmd(linkCmd: string) =
|
||||
tryExceptOSErrorMessage("invocation of external linker program failed."):
|
||||
execExternalProgram(linkCmd,
|
||||
if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking)
|
||||
|
||||
proc execCmdsInParallel(cmds: seq[string]; prettyCb: proc (idx: int)) =
|
||||
let runCb = proc (idx: int, p: Process) =
|
||||
let exitCode = p.peekExitCode
|
||||
if exitCode != 0:
|
||||
rawMessage(errGenerated, "execution of an external compiler program '" &
|
||||
cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" &
|
||||
p.outputStream.readAll.strip)
|
||||
if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
|
||||
var res = 0
|
||||
if gNumberOfProcessors <= 1:
|
||||
for i in countup(0, high(cmds)):
|
||||
tryExceptOSErrorMessage("invocation of external compiler program failed."):
|
||||
res = execWithEcho(cmds[i])
|
||||
if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i])
|
||||
else:
|
||||
tryExceptOSErrorMessage("invocation of external compiler program failed."):
|
||||
if optListCmd in gGlobalOptions or gVerbosity > 1:
|
||||
res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams},
|
||||
gNumberOfProcessors, afterRunEvent=runCb)
|
||||
elif gVerbosity == 1:
|
||||
res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
|
||||
gNumberOfProcessors, prettyCb, afterRunEvent=runCb)
|
||||
else:
|
||||
res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
|
||||
gNumberOfProcessors, afterRunEvent=runCb)
|
||||
if res != 0:
|
||||
if gNumberOfProcessors <= 1:
|
||||
rawMessage(errExecutionOfProgramFailed, cmds.join())
|
||||
|
||||
proc callCCompiler*(projectfile: string) =
|
||||
var
|
||||
linkCmd: string
|
||||
@@ -713,35 +748,9 @@ proc callCCompiler*(projectfile: string) =
|
||||
var prettyCmds: TStringSeq = @[]
|
||||
let prettyCb = proc (idx: int) =
|
||||
echo prettyCmds[idx]
|
||||
let runCb = proc (idx: int, p: Process) =
|
||||
let exitCode = p.peekExitCode
|
||||
if exitCode != 0:
|
||||
rawMessage(errGenerated, "execution of an external compiler program '" &
|
||||
cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" &
|
||||
p.outputStream.readAll.strip)
|
||||
compileCFile(toCompile, script, cmds, prettyCmds)
|
||||
if optCompileOnly notin gGlobalOptions:
|
||||
if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
|
||||
var res = 0
|
||||
if gNumberOfProcessors <= 1:
|
||||
for i in countup(0, high(cmds)):
|
||||
tryExceptOSErrorMessage("invocation of external compiler program failed."):
|
||||
res = execWithEcho(cmds[i])
|
||||
if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i])
|
||||
else:
|
||||
tryExceptOSErrorMessage("invocation of external compiler program failed."):
|
||||
if optListCmd in gGlobalOptions or gVerbosity > 1:
|
||||
res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams},
|
||||
gNumberOfProcessors, afterRunEvent=runCb)
|
||||
elif gVerbosity == 1:
|
||||
res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
|
||||
gNumberOfProcessors, prettyCb, afterRunEvent=runCb)
|
||||
else:
|
||||
res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
|
||||
gNumberOfProcessors, afterRunEvent=runCb)
|
||||
if res != 0:
|
||||
if gNumberOfProcessors <= 1:
|
||||
rawMessage(errExecutionOfProgramFailed, cmds.join())
|
||||
execCmdsInParallel(cmds, prettyCb)
|
||||
if optNoLinking notin gGlobalOptions:
|
||||
# call the linker:
|
||||
var objfiles = ""
|
||||
@@ -756,9 +765,7 @@ proc callCCompiler*(projectfile: string) =
|
||||
|
||||
linkCmd = getLinkCmd(projectfile, objfiles)
|
||||
if optCompileOnly notin gGlobalOptions:
|
||||
tryExceptOSErrorMessage("invocation of external linker program failed."):
|
||||
execExternalProgram(linkCmd,
|
||||
if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking)
|
||||
execLinkCmd(linkCmd)
|
||||
else:
|
||||
linkCmd = ""
|
||||
if optGenScript in gGlobalOptions:
|
||||
@@ -766,7 +773,8 @@ proc callCCompiler*(projectfile: string) =
|
||||
add(script, tnl)
|
||||
generateScript(projectfile, script)
|
||||
|
||||
from json import escapeJson
|
||||
#from json import escapeJson
|
||||
import json
|
||||
|
||||
proc writeJsonBuildInstructions*(projectfile: string) =
|
||||
template lit(x: untyped) = f.write x
|
||||
@@ -834,6 +842,34 @@ proc writeJsonBuildInstructions*(projectfile: string) =
|
||||
lit "\L}\L"
|
||||
close(f)
|
||||
|
||||
proc runJsonBuildInstructions*(projectfile: string) =
|
||||
let file = projectfile.splitFile.name
|
||||
let jsonFile = toGeneratedFile(file, "json")
|
||||
try:
|
||||
let data = json.parseFile(jsonFile)
|
||||
let toCompile = data["compile"]
|
||||
doAssert toCompile.kind == JArray
|
||||
var cmds: TStringSeq = @[]
|
||||
var prettyCmds: TStringSeq = @[]
|
||||
for c in toCompile:
|
||||
doAssert c.kind == JArray
|
||||
doAssert c.len >= 2
|
||||
|
||||
add(cmds, c[1].getStr)
|
||||
let (_, name, _) = splitFile(c[0].getStr)
|
||||
add(prettyCmds, "CC: " & name)
|
||||
|
||||
let prettyCb = proc (idx: int) =
|
||||
echo prettyCmds[idx]
|
||||
execCmdsInParallel(cmds, prettyCb)
|
||||
|
||||
let linkCmd = data["linkcmd"]
|
||||
doAssert linkCmd.kind == JString
|
||||
execLinkCmd(linkCmd.getStr)
|
||||
except:
|
||||
echo getCurrentException().getStackTrace()
|
||||
quit "error evaluating JSON file: " & jsonFile
|
||||
|
||||
proc genMappingFiles(list: CFileList): Rope =
|
||||
for it in list:
|
||||
addf(result, "--file:r\"$1\"$N", [rope(it.cname)])
|
||||
|
||||
83
compiler/gorgeimpl.nim
Normal file
83
compiler/gorgeimpl.nim
Normal file
@@ -0,0 +1,83 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2017 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Module that implements ``gorge`` for the compiler as well as
|
||||
## the scriptable import mechanism.
|
||||
|
||||
import msgs, securehash, os, osproc, streams, strutils, options
|
||||
|
||||
proc readOutput(p: Process): (string, int) =
|
||||
result[0] = ""
|
||||
var output = p.outputStream
|
||||
while not output.atEnd:
|
||||
result[0].add(output.readLine)
|
||||
result[0].add("\n")
|
||||
if result[0].len > 0:
|
||||
result[0].setLen(result[0].len - "\n".len)
|
||||
result[1] = p.waitForExit
|
||||
|
||||
proc opGorge*(cmd, input, cache: string, info: TLineInfo): (string, int) =
|
||||
let workingDir = parentDir(info.toFullPath)
|
||||
if cache.len > 0:# and optForceFullMake notin gGlobalOptions:
|
||||
let h = secureHash(cmd & "\t" & input & "\t" & cache)
|
||||
let filename = options.toGeneratedFile("gorge_" & $h, "txt")
|
||||
var f: File
|
||||
if open(f, filename):
|
||||
result = (f.readAll, 0)
|
||||
f.close
|
||||
return
|
||||
var readSuccessful = false
|
||||
try:
|
||||
var p = startProcess(cmd, workingDir,
|
||||
options={poEvalCommand, poStderrToStdout})
|
||||
if input.len != 0:
|
||||
p.inputStream.write(input)
|
||||
p.inputStream.close()
|
||||
result = p.readOutput
|
||||
readSuccessful = true
|
||||
# only cache successful runs:
|
||||
if result[1] == 0:
|
||||
writeFile(filename, result[0])
|
||||
except IOError, OSError:
|
||||
if not readSuccessful: result = ("", -1)
|
||||
else:
|
||||
try:
|
||||
var p = startProcess(cmd, workingDir,
|
||||
options={poEvalCommand, poStderrToStdout})
|
||||
if input.len != 0:
|
||||
p.inputStream.write(input)
|
||||
p.inputStream.close()
|
||||
result = p.readOutput
|
||||
except IOError, OSError:
|
||||
result = ("", -1)
|
||||
|
||||
proc scriptableImport*(pkg, subdir: string; info: TLineInfo): string =
|
||||
var cmd = getConfigVar("resolver.exe")
|
||||
if cmd.len == 0: cmd = "nimresolve"
|
||||
else: cmd = quoteShell(cmd)
|
||||
cmd.add " --source:"
|
||||
cmd.add quoteShell(info.toFullPath())
|
||||
cmd.add " --stdlib:"
|
||||
cmd.add quoteShell(options.libpath)
|
||||
cmd.add " --project:"
|
||||
cmd.add quoteShell(gProjectFull)
|
||||
if subdir.len != 0:
|
||||
cmd.add " --subdir:"
|
||||
cmd.add quoteShell(subdir)
|
||||
if options.gNoNimblePath:
|
||||
cmd.add " --nonimblepath"
|
||||
cmd.add ' '
|
||||
cmd.add quoteShell(pkg)
|
||||
let (res, exitCode) = opGorge(cmd, "", cmd, info)
|
||||
if exitCode == 0:
|
||||
result = res.strip()
|
||||
elif res.len > 0:
|
||||
localError(info, res)
|
||||
else:
|
||||
localError(info, "cannot resolve: " & (pkg / subdir))
|
||||
@@ -11,11 +11,22 @@
|
||||
|
||||
import
|
||||
intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups,
|
||||
semdata, passes, renderer
|
||||
semdata, passes, renderer, gorgeimpl
|
||||
|
||||
proc evalImport*(c: PContext, n: PNode): PNode
|
||||
proc evalFrom*(c: PContext, n: PNode): PNode
|
||||
|
||||
proc lookupPackage(pkg, subdir: PNode): string =
|
||||
let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: ""
|
||||
case pkg.kind
|
||||
of nkStrLit, nkRStrLit, nkTripleStrLit:
|
||||
result = scriptableImport(pkg.strVal, sub, pkg.info)
|
||||
of nkIdent:
|
||||
result = scriptableImport(pkg.ident.s, sub, pkg.info)
|
||||
else:
|
||||
localError(pkg.info, "package name must be an identifier or string literal")
|
||||
result = ""
|
||||
|
||||
proc getModuleName*(n: PNode): string =
|
||||
# This returns a short relative module name without the nim extension
|
||||
# e.g. like "system", "importer" or "somepath/module"
|
||||
@@ -31,16 +42,33 @@ proc getModuleName*(n: PNode): string =
|
||||
result = n.ident.s
|
||||
of nkSym:
|
||||
result = n.sym.name.s
|
||||
of nkInfix, nkPrefix:
|
||||
if n.sons[0].kind == nkIdent and n.sons[0].ident.id == getIdent("as").id:
|
||||
of nkInfix:
|
||||
let n0 = n[0]
|
||||
let n1 = n[1]
|
||||
if n0.kind == nkIdent and n0.ident.id == getIdent("as").id:
|
||||
# XXX hack ahead:
|
||||
n.kind = nkImportAs
|
||||
n.sons[0] = n.sons[1]
|
||||
n.sons[1] = n.sons[2]
|
||||
n.sons.setLen(2)
|
||||
return getModuleName(n.sons[0])
|
||||
# hacky way to implement 'x / y /../ z':
|
||||
result = renderTree(n, {renderNoComments}).replace(" ")
|
||||
if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$":
|
||||
if n0.kind == nkIdent and n0.ident.s == "/":
|
||||
result = lookupPackage(n1[1], n[2])
|
||||
else:
|
||||
localError(n.info, "only '/' supported with $package notation")
|
||||
result = ""
|
||||
else:
|
||||
# hacky way to implement 'x / y /../ z':
|
||||
result = getModuleName(n1)
|
||||
result.add renderTree(n0, {renderNoComments})
|
||||
result.add getModuleName(n[2])
|
||||
of nkPrefix:
|
||||
if n.sons[0].kind == nkIdent and n.sons[0].ident.s == "$":
|
||||
result = lookupPackage(n[1], nil)
|
||||
else:
|
||||
# hacky way to implement 'x / y /../ z':
|
||||
result = renderTree(n, {renderNoComments}).replace(" ")
|
||||
of nkDotExpr:
|
||||
result = renderTree(n, {renderNoComments}).replace(".", "/")
|
||||
of nkImportAs:
|
||||
@@ -60,6 +88,11 @@ proc checkModuleName*(n: PNode; doLocalError=true): int32 =
|
||||
else:
|
||||
result = fullPath.fileInfoIdx
|
||||
|
||||
proc importPureEnumField*(c: PContext; s: PSym) =
|
||||
var check = strTableGet(c.importTable.symbols, s.name)
|
||||
if check == nil:
|
||||
strTableAdd(c.pureEnumFields, s)
|
||||
|
||||
proc rawImportSymbol(c: PContext, s: PSym) =
|
||||
# This does not handle stubs, because otherwise loading on demand would be
|
||||
# pointless in practice. So importing stubs is fine here!
|
||||
@@ -75,7 +108,7 @@ proc rawImportSymbol(c: PContext, s: PSym) =
|
||||
strTableAdd(c.importTable.symbols, s)
|
||||
if s.kind == skType:
|
||||
var etyp = s.typ
|
||||
if etyp.kind in {tyBool, tyEnum} and sfPure notin s.flags:
|
||||
if etyp.kind in {tyBool, tyEnum}:
|
||||
for j in countup(0, sonsLen(etyp.n) - 1):
|
||||
var e = etyp.n.sons[j].sym
|
||||
if e.kind != skEnumField:
|
||||
@@ -91,7 +124,10 @@ proc rawImportSymbol(c: PContext, s: PSym) =
|
||||
break
|
||||
check = nextIdentIter(it, c.importTable.symbols)
|
||||
if e != nil:
|
||||
rawImportSymbol(c, e)
|
||||
if sfPure notin s.flags:
|
||||
rawImportSymbol(c, e)
|
||||
else:
|
||||
importPureEnumField(c, e)
|
||||
else:
|
||||
# rodgen assures that converters and patterns are no stubs
|
||||
if s.kind == skConverter: addConverter(c, s)
|
||||
@@ -201,12 +237,14 @@ proc evalImport(c: PContext, n: PNode): PNode =
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
let it = n.sons[i]
|
||||
if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
|
||||
let sep = renderTree(it.sons[0], {renderNoComments})
|
||||
let dir = renderTree(it.sons[1], {renderNoComments})
|
||||
let sep = it[0]
|
||||
let dir = it[1]
|
||||
let a = newNodeI(nkInfix, it.info)
|
||||
a.add sep
|
||||
a.add dir
|
||||
a.add sep # dummy entry, replaced in the loop
|
||||
for x in it[2]:
|
||||
let f = renderTree(x, {renderNoComments})
|
||||
let a = newStrNode(nkStrLit, (dir & sep & f).replace(" "))
|
||||
a.info = it.info
|
||||
a.sons[2] = x
|
||||
impMod(c, a)
|
||||
else:
|
||||
impMod(c, it)
|
||||
|
||||
@@ -187,12 +187,12 @@ proc mapType(typ: PType): TJSTypeKind =
|
||||
of tyBool: result = etyBool
|
||||
of tyFloat..tyFloat128: result = etyFloat
|
||||
of tySet: result = etyObject # map a set to a table
|
||||
of tyString, tySequence: result = etySeq
|
||||
of tyString, tySequence, tyOpt: result = etySeq
|
||||
of tyObject, tyArray, tyTuple, tyOpenArray, tyVarargs:
|
||||
result = etyObject
|
||||
of tyNil: result = etyNull
|
||||
of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvocation,
|
||||
tyNone, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor,
|
||||
tyNone, tyFromExpr, tyForward, tyEmpty,
|
||||
tyExpr, tyStmt, tyTypeDesc, tyTypeClasses, tyVoid, tyAlias:
|
||||
result = etyNone
|
||||
of tyInferred:
|
||||
@@ -202,7 +202,7 @@ proc mapType(typ: PType): TJSTypeKind =
|
||||
else: result = etyNone
|
||||
of tyProc: result = etyProc
|
||||
of tyCString: result = etyString
|
||||
of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("mapType")
|
||||
of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("mapType")
|
||||
|
||||
proc mapType(p: PProc; typ: PType): TJSTypeKind =
|
||||
if p.target == targetPHP: result = etyObject
|
||||
@@ -292,7 +292,7 @@ proc useMagic(p: PProc, name: string) =
|
||||
if name.len == 0: return
|
||||
var s = magicsys.getCompilerProc(name)
|
||||
if s != nil:
|
||||
internalAssert s.kind in {skProc, skMethod, skConverter}
|
||||
internalAssert s.kind in {skProc, skFunc, skMethod, skConverter}
|
||||
if not p.g.generatedSyms.containsOrIncl(s.id):
|
||||
let code = genProc(p, s)
|
||||
add(p.g.constants, code)
|
||||
@@ -927,7 +927,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
|
||||
|
||||
# we don't care if it's an etyBaseIndex (global) of a string, it's
|
||||
# still a string that needs to be copied properly:
|
||||
if x.typ.skipTypes(abstractInst).kind in {tySequence, tyString}:
|
||||
if x.typ.skipTypes(abstractInst).kind in {tySequence, tyOpt, tyString}:
|
||||
xtyp = etySeq
|
||||
case xtyp
|
||||
of etySeq:
|
||||
@@ -971,7 +971,7 @@ proc genFastAsgn(p: PProc, n: PNode) =
|
||||
# See bug #5933. So we try to be more compatible with the C backend semantics
|
||||
# here for 'shallowCopy'. This is an educated guess and might require further
|
||||
# changes later:
|
||||
let noCopy = n[0].typ.skipTypes(abstractInst).kind in {tySequence, tyString}
|
||||
let noCopy = n[0].typ.skipTypes(abstractInst).kind in {tySequence, tyOpt, tyString}
|
||||
genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=noCopy)
|
||||
|
||||
proc genSwap(p: PProc, n: PNode) =
|
||||
@@ -1111,7 +1111,7 @@ template isIndirect(x: PSym): bool =
|
||||
({sfAddrTaken, sfGlobal} * v.flags != {} and
|
||||
#(mapType(v.typ) != etyObject) and
|
||||
{sfImportc, sfVolatile, sfExportc} * v.flags == {} and
|
||||
v.kind notin {skProc, skConverter, skMethod, skIterator,
|
||||
v.kind notin {skProc, skFunc, skConverter, skMethod, skIterator,
|
||||
skConst, skTemp, skLet} and p.target == targetJS)
|
||||
|
||||
proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
@@ -1237,7 +1237,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
|
||||
else:
|
||||
r.res = "$" & s.loc.r
|
||||
p.declareGlobal(s.id, r.res)
|
||||
of skProc, skConverter, skMethod:
|
||||
of skProc, skFunc, skConverter, skMethod:
|
||||
discard mangleName(s, p.target)
|
||||
if p.target == targetPHP and r.kind != resCallee:
|
||||
r.res = makeJsString($s.loc.r)
|
||||
@@ -1550,7 +1550,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||
result = putToSeq("[null, 0]", indirect)
|
||||
else:
|
||||
result = putToSeq("null", indirect)
|
||||
of tySequence, tyString, tyCString, tyPointer, tyProc:
|
||||
of tySequence, tyOpt, tyString, tyCString, tyPointer, tyProc:
|
||||
result = putToSeq("null", indirect)
|
||||
of tyStatic:
|
||||
if t.n != nil:
|
||||
@@ -2338,7 +2338,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
|
||||
nkFromStmt, nkTemplateDef, nkMacroDef: discard
|
||||
of nkPragma: genPragma(p, n)
|
||||
of nkProcDef, nkMethodDef, nkConverterDef:
|
||||
of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef:
|
||||
var s = n.sons[namePos].sym
|
||||
if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}:
|
||||
genSym(p, n.sons[namePos], r)
|
||||
|
||||
@@ -194,7 +194,7 @@ proc illegalCapture(s: PSym): bool {.inline.} =
|
||||
s.kind == skResult
|
||||
|
||||
proc isInnerProc(s: PSym): bool =
|
||||
if s.kind in {skProc, skMethod, skConverter, skIterator} and s.magic == mNone:
|
||||
if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone:
|
||||
result = s.skipGenericOwner.kind in routineKinds
|
||||
|
||||
proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
|
||||
@@ -371,7 +371,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
let s = n.sym
|
||||
if s.kind in {skProc, skMethod, skConverter, skIterator} and s.typ != nil and s.typ.callConv == ccClosure:
|
||||
if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and
|
||||
s.typ != nil and s.typ.callConv == ccClosure:
|
||||
# this handles the case that the inner proc was declared as
|
||||
# .closure but does not actually capture anything:
|
||||
addClosureParam(c, s, n.info)
|
||||
@@ -443,7 +444,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
discard
|
||||
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef:
|
||||
discard
|
||||
of nkLambdaKinds, nkIteratorDef:
|
||||
of nkLambdaKinds, nkIteratorDef, nkFuncDef:
|
||||
if n.typ != nil:
|
||||
detectCapturedVars(n[namePos], owner, c)
|
||||
else:
|
||||
@@ -730,7 +731,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
|
||||
# now we know better, so patch it:
|
||||
n.sons[0] = x.sons[0]
|
||||
n.sons[1] = x.sons[1]
|
||||
of nkLambdaKinds, nkIteratorDef:
|
||||
of nkLambdaKinds, nkIteratorDef, nkFuncDef:
|
||||
if n.typ != nil and n[namePos].kind == nkSym:
|
||||
let m = newSymNode(n[namePos].sym)
|
||||
m.typ = n.typ
|
||||
|
||||
@@ -48,7 +48,7 @@ type
|
||||
tkShl, tkShr, tkStatic,
|
||||
tkTemplate,
|
||||
tkTry, tkTuple, tkType, tkUsing,
|
||||
tkVar, tkWhen, tkWhile, tkWith, tkWithout, tkXor,
|
||||
tkVar, tkWhen, tkWhile, tkXor,
|
||||
tkYield, # end of keywords
|
||||
tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit,
|
||||
tkUIntLit, tkUInt8Lit, tkUInt16Lit, tkUInt32Lit, tkUInt64Lit,
|
||||
@@ -89,7 +89,7 @@ const
|
||||
"shl", "shr", "static",
|
||||
"template",
|
||||
"try", "tuple", "type", "using",
|
||||
"var", "when", "while", "with", "without", "xor",
|
||||
"var", "when", "while", "xor",
|
||||
"yield",
|
||||
"tkIntLit", "tkInt8Lit", "tkInt16Lit", "tkInt32Lit", "tkInt64Lit",
|
||||
"tkUIntLit", "tkUInt8Lit", "tkUInt16Lit", "tkUInt32Lit", "tkUInt64Lit",
|
||||
|
||||
@@ -144,8 +144,10 @@ type
|
||||
|
||||
proc getSymRepr*(s: PSym): string =
|
||||
case s.kind
|
||||
of skProc, skMethod, skConverter, skIterator: result = getProcHeader(s)
|
||||
else: result = s.name.s
|
||||
of skProc, skFunc, skMethod, skConverter, skIterator:
|
||||
result = getProcHeader(s)
|
||||
else:
|
||||
result = s.name.s
|
||||
|
||||
proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
|
||||
# check if all symbols have been used and defined:
|
||||
@@ -286,7 +288,7 @@ proc lookUp*(c: PContext, n: PNode): PSym =
|
||||
|
||||
type
|
||||
TLookupFlag* = enum
|
||||
checkAmbiguity, checkUndeclared, checkModule
|
||||
checkAmbiguity, checkUndeclared, checkModule, checkPureEnumFields
|
||||
|
||||
proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
const allExceptModule = {low(TSymKind)..high(TSymKind)}-{skModule,skPackage}
|
||||
@@ -297,6 +299,8 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
result = searchInScopes(c, ident).skipAlias(n)
|
||||
else:
|
||||
result = searchInScopes(c, ident, allExceptModule).skipAlias(n)
|
||||
if result == nil and checkPureEnumFields in flags:
|
||||
result = strTableGet(c.pureEnumFields, ident)
|
||||
if result == nil and checkUndeclared in flags:
|
||||
fixSpelling(n, ident, searchInScopes)
|
||||
errorUndeclaredIdentifier(c, n.info, ident.s)
|
||||
|
||||
@@ -633,7 +633,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
|
||||
if fn.kind == nkClosure:
|
||||
localError(n.info, "closure in spawn environment is not allowed")
|
||||
if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro,
|
||||
skMethod, skConverter}):
|
||||
skFunc, skMethod, skConverter}):
|
||||
# for indirect calls we pass the function pointer in the scratchObj
|
||||
var argType = n[0].typ.skipTypes(abstractInst)
|
||||
var field = newSym(skField, getIdent"fn", owner, n.info)
|
||||
|
||||
@@ -78,6 +78,10 @@ proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) =
|
||||
extccomp.callCCompiler(proj)
|
||||
extccomp.writeJsonBuildInstructions(proj)
|
||||
|
||||
proc commandJsonScript(graph: ModuleGraph; cache: IdentCache) =
|
||||
let proj = changeFileExt(gProjectFull, "")
|
||||
extccomp.runJsonBuildInstructions(proj)
|
||||
|
||||
proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) =
|
||||
#incl(gGlobalOptions, optSafeCode)
|
||||
setTarget(osJS, cpuJS)
|
||||
@@ -266,6 +270,9 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
|
||||
of "nop", "help":
|
||||
# prevent the "success" message:
|
||||
gCmd = cmdDump
|
||||
of "jsonscript":
|
||||
gCmd = cmdJsonScript
|
||||
commandJsonScript(graph, cache)
|
||||
else:
|
||||
rawMessage(errInvalidCommandX, command)
|
||||
|
||||
|
||||
@@ -119,4 +119,6 @@ condsyms.initDefines()
|
||||
|
||||
when not defined(selftest):
|
||||
handleCmdLine(newIdentCache(), newConfigRef())
|
||||
when declared(GC_setMaxPause):
|
||||
echo GC_getStatistics()
|
||||
msgQuit(int8(msgs.gErrorCounter > 0))
|
||||
|
||||
@@ -16,11 +16,11 @@ proc addPath*(path: string, info: TLineInfo) =
|
||||
options.searchPaths.insert(path, 0)
|
||||
|
||||
type
|
||||
Version = distinct string
|
||||
Version* = distinct string
|
||||
|
||||
proc `$`(ver: Version): string {.borrow.}
|
||||
proc `$`*(ver: Version): string {.borrow.}
|
||||
|
||||
proc newVersion(ver: string): Version =
|
||||
proc newVersion*(ver: string): Version =
|
||||
doAssert(ver.len == 0 or ver[0] in {'#', '\0'} + Digits,
|
||||
"Wrong version: " & ver)
|
||||
return Version(ver)
|
||||
@@ -28,7 +28,7 @@ proc newVersion(ver: string): Version =
|
||||
proc isSpecial(ver: Version): bool =
|
||||
return ($ver).len > 0 and ($ver)[0] == '#'
|
||||
|
||||
proc `<`(ver: Version, ver2: Version): bool =
|
||||
proc `<`*(ver: Version, ver2: Version): bool =
|
||||
## This is synced from Nimble's version module.
|
||||
|
||||
# Handling for special versions such as "#head" or "#branch".
|
||||
|
||||
@@ -91,7 +91,8 @@ type
|
||||
cmdRst2html, # convert a reStructuredText file to HTML
|
||||
cmdRst2tex, # convert a reStructuredText file to TeX
|
||||
cmdInteractive, # start interactive session
|
||||
cmdRun # run the project via TCC backend
|
||||
cmdRun, # run the project via TCC backend
|
||||
cmdJsonScript # compile a .json build file
|
||||
TStringSeq* = seq[string]
|
||||
TGCMode* = enum # the selected GC
|
||||
gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcRefc,
|
||||
|
||||
@@ -255,13 +255,6 @@ proc isUnary(p: TParser): bool =
|
||||
p.tok.strongSpaceB == 0 and
|
||||
p.tok.strongSpaceA > 0:
|
||||
result = true
|
||||
# versions prior to 0.13.0 used to do this:
|
||||
when false:
|
||||
if p.strongSpaces:
|
||||
result = true
|
||||
else:
|
||||
parMessage(p, warnDeprecated,
|
||||
"will be parsed as unary operator; inconsistent spacing")
|
||||
|
||||
proc checkBinary(p: TParser) {.inline.} =
|
||||
## Check if the current parser token is a binary operator.
|
||||
@@ -700,12 +693,7 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
|
||||
result = r
|
||||
|
||||
template somePar() =
|
||||
if p.tok.strongSpaceA > 0:
|
||||
if p.strongSpaces:
|
||||
break
|
||||
else:
|
||||
parMessage(p, warnDeprecated,
|
||||
"a [b] will be parsed as command syntax; spacing")
|
||||
if p.tok.strongSpaceA > 0: break
|
||||
# progress guaranteed
|
||||
while p.tok.indent < 0 or
|
||||
(p.tok.tokType == tkDot and p.tok.indent >= baseIndent):
|
||||
@@ -991,7 +979,7 @@ proc parseDoBlock(p: var TParser; info: TLineInfo): PNode =
|
||||
if params.kind != nkEmpty:
|
||||
result = newProcNode(nkDo, info, result, params = params, pragmas = pragmas)
|
||||
|
||||
proc parseProcExpr(p: var TParser, isExpr: bool): PNode =
|
||||
proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode =
|
||||
#| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)?
|
||||
# either a proc type or a anonymous proc
|
||||
let info = parLineInfo(p)
|
||||
@@ -1002,7 +990,7 @@ proc parseProcExpr(p: var TParser, isExpr: bool): PNode =
|
||||
if p.tok.tokType == tkEquals and isExpr:
|
||||
getTok(p)
|
||||
skipComment(p, result)
|
||||
result = newProcNode(nkLambda, info, parseStmt(p),
|
||||
result = newProcNode(kind, info, parseStmt(p),
|
||||
params = params,
|
||||
pragmas = pragmas)
|
||||
else:
|
||||
@@ -1014,7 +1002,7 @@ proc parseProcExpr(p: var TParser, isExpr: bool): PNode =
|
||||
proc isExprStart(p: TParser): bool =
|
||||
case p.tok.tokType
|
||||
of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf,
|
||||
tkProc, tkIterator, tkBind, tkAddr,
|
||||
tkProc, tkFunc, tkIterator, tkBind, tkAddr,
|
||||
tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr,
|
||||
tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut:
|
||||
result = true
|
||||
@@ -1038,9 +1026,15 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
|
||||
optInd(p, result)
|
||||
if not isOperator(p.tok) and isExprStart(p):
|
||||
addSon(result, primary(p, mode))
|
||||
if kind == nkDistinctTy and p.tok.tokType in {tkWith, tkWithout}:
|
||||
let nodeKind = if p.tok.tokType == tkWith: nkWith
|
||||
else: nkWithout
|
||||
if kind == nkDistinctTy and p.tok.tokType == tkSymbol:
|
||||
# XXX document this feature!
|
||||
var nodeKind: TNodeKind
|
||||
if p.tok.ident.s == "with":
|
||||
nodeKind = nkWith
|
||||
elif p.tok.ident.s == "without":
|
||||
nodeKind = nkWithout
|
||||
else:
|
||||
return result
|
||||
getTok(p)
|
||||
let list = newNodeP(nodeKind, p)
|
||||
result.addSon list
|
||||
@@ -1088,21 +1082,12 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||
|
||||
case p.tok.tokType:
|
||||
of tkTuple: result = parseTuple(p, mode == pmTypeDef)
|
||||
of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef})
|
||||
of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkLambda)
|
||||
of tkFunc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkFuncDef)
|
||||
of tkIterator:
|
||||
when false:
|
||||
if mode in {pmTypeDesc, pmTypeDef}:
|
||||
result = parseProcExpr(p, false)
|
||||
result.kind = nkIteratorTy
|
||||
else:
|
||||
# no anon iterators for now:
|
||||
parMessage(p, errExprExpected, p.tok)
|
||||
getTok(p) # we must consume a token here to prevend endless loops!
|
||||
result = ast.emptyNode
|
||||
else:
|
||||
result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef})
|
||||
if result.kind == nkLambda: result.kind = nkIteratorDef
|
||||
else: result.kind = nkIteratorTy
|
||||
result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkLambda)
|
||||
if result.kind == nkLambda: result.kind = nkIteratorDef
|
||||
else: result.kind = nkIteratorTy
|
||||
of tkEnum:
|
||||
if mode == pmTypeDef:
|
||||
result = parseEnum(p)
|
||||
@@ -2000,6 +1985,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
|
||||
of tkDefer: result = parseStaticOrDefer(p, nkDefer)
|
||||
of tkAsm: result = parseAsm(p)
|
||||
of tkProc: result = parseRoutine(p, nkProcDef)
|
||||
of tkFunc: result = parseRoutine(p, nkFuncDef)
|
||||
of tkMethod: result = parseRoutine(p, nkMethodDef)
|
||||
of tkIterator: result = parseRoutine(p, nkIteratorDef)
|
||||
of tkMacro: result = parseRoutine(p, nkMacroDef)
|
||||
@@ -2056,8 +2042,8 @@ proc parseStmt(p: var TParser): PNode =
|
||||
else:
|
||||
# the case statement is only needed for better error messages:
|
||||
case p.tok.tokType
|
||||
of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkIterator,
|
||||
tkMacro, tkType, tkConst, tkWhen, tkVar:
|
||||
of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkFunc,
|
||||
tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar:
|
||||
parMessage(p, errComplexStmtRequiresInd)
|
||||
result = ast.emptyNode
|
||||
else:
|
||||
|
||||
@@ -64,7 +64,7 @@ proc astNeeded*(s: PSym): bool =
|
||||
# needs to be stored. The passes manager frees s.sons[codePos] when
|
||||
# appropriate to free the procedure body's memory. This is important
|
||||
# to keep memory usage down.
|
||||
if (s.kind in {skMethod, skProc}) and
|
||||
if (s.kind in {skMethod, skProc, skFunc}) and
|
||||
({sfCompilerProc, sfCompileTime} * s.flags == {}) and
|
||||
(s.typ.callConv != ccInline) and
|
||||
(s.ast.sons[genericParamsPos].kind == nkEmpty):
|
||||
|
||||
@@ -906,9 +906,14 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
|
||||
optInd(p, result)
|
||||
if not isOperator(p.tok) and isExprStart(p):
|
||||
addSon(result, primary(p, mode))
|
||||
if kind == nkDistinctTy and p.tok.tokType in {tkWith, tkWithout}:
|
||||
let nodeKind = if p.tok.tokType == tkWith: nkWith
|
||||
else: nkWithout
|
||||
if kind == nkDistinctTy and p.tok.tokType == tkSymbol:
|
||||
var nodeKind: TNodeKind
|
||||
if p.tok.ident.s == "with":
|
||||
nodeKind = nkWith
|
||||
elif p.tok.ident.s == "without":
|
||||
nodeKind = nkWithout
|
||||
else:
|
||||
return result
|
||||
getTok(p)
|
||||
let list = newNodeP(nodeKind, p)
|
||||
result.addSon list
|
||||
|
||||
@@ -1079,9 +1079,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
gsub(g, n.sons[0])
|
||||
if n.len > 1:
|
||||
if n[1].kind == nkWith:
|
||||
putWithSpace(g, tkWith, " with")
|
||||
putWithSpace(g, tkSymbol, " with")
|
||||
else:
|
||||
putWithSpace(g, tkWithout, " without")
|
||||
putWithSpace(g, tkSymbol, " without")
|
||||
gcomma(g, n[1])
|
||||
else:
|
||||
put(g, tkDistinct, "distinct")
|
||||
@@ -1166,6 +1166,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
of nkProcDef:
|
||||
if renderNoProcDefs notin g.flags: putWithSpace(g, tkProc, "proc")
|
||||
gproc(g, n)
|
||||
of nkFuncDef:
|
||||
if renderNoProcDefs notin g.flags: putWithSpace(g, tkFunc, "func")
|
||||
gproc(g, n)
|
||||
of nkConverterDef:
|
||||
if renderNoProcDefs notin g.flags: putWithSpace(g, tkConverter, "converter")
|
||||
gproc(g, n)
|
||||
@@ -1324,7 +1327,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
if p.typ == nil or tfImplicitTypeParam notin p.typ.flags:
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
if n.hasExplicitParams:
|
||||
put(g, tkBracketLe, "[")
|
||||
gsemicolon(g, n)
|
||||
@@ -1363,7 +1366,13 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
proc renderTree(n: PNode, renderFlags: TRenderFlags = {}): string =
|
||||
var g: TSrcGen
|
||||
initSrcGen(g, renderFlags)
|
||||
gsub(g, n)
|
||||
# do not indent the initial statement list so that
|
||||
# writeFile("file.nim", repr n)
|
||||
# produces working Nim code:
|
||||
if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
|
||||
gstmts(g, n, emptyContext, doIndent = false)
|
||||
else:
|
||||
gsub(g, n)
|
||||
result = g.buf
|
||||
|
||||
proc renderModule(n: PNode, filename: string,
|
||||
|
||||
@@ -257,9 +257,9 @@ proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
|
||||
loc.k = low(loc.k)
|
||||
if r.s[r.pos] == '*':
|
||||
inc(r.pos)
|
||||
loc.s = TStorageLoc(decodeVInt(r.s, r.pos))
|
||||
loc.storage = TStorageLoc(decodeVInt(r.s, r.pos))
|
||||
else:
|
||||
loc.s = low(loc.s)
|
||||
loc.storage = low(loc.storage)
|
||||
if r.s[r.pos] == '$':
|
||||
inc(r.pos)
|
||||
loc.flags = cast[TLocFlags](int32(decodeVInt(r.s, r.pos)))
|
||||
@@ -267,9 +267,10 @@ proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
|
||||
loc.flags = {}
|
||||
if r.s[r.pos] == '^':
|
||||
inc(r.pos)
|
||||
loc.t = rrGetType(r, decodeVInt(r.s, r.pos), info)
|
||||
loc.lode = decodeNode(r, info)
|
||||
# rrGetType(r, decodeVInt(r.s, r.pos), info)
|
||||
else:
|
||||
loc.t = nil
|
||||
loc.lode = nil
|
||||
if r.s[r.pos] == '!':
|
||||
inc(r.pos)
|
||||
loc.r = rope(decodeStr(r.s, r.pos))
|
||||
@@ -588,7 +589,7 @@ proc cmdChangeTriggersRecompilation(old, new: TCommands): bool =
|
||||
return false
|
||||
of cmdNone, cmdDoc, cmdInterpret, cmdPretty, cmdGenDepend, cmdDump,
|
||||
cmdCheck, cmdParse, cmdScan, cmdIdeTools, cmdDef,
|
||||
cmdRst2html, cmdRst2tex, cmdInteractive, cmdRun:
|
||||
cmdRst2html, cmdRst2tex, cmdInteractive, cmdRun, cmdJsonScript:
|
||||
discard
|
||||
# else: trigger recompilation:
|
||||
result = true
|
||||
|
||||
@@ -175,16 +175,17 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
|
||||
var oldLen = result.len
|
||||
result.add('<')
|
||||
if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
|
||||
if loc.s != low(loc.s):
|
||||
if loc.storage != low(loc.storage):
|
||||
add(result, '*')
|
||||
encodeVInt(ord(loc.s), result)
|
||||
encodeVInt(ord(loc.storage), result)
|
||||
if loc.flags != {}:
|
||||
add(result, '$')
|
||||
encodeVInt(cast[int32](loc.flags), result)
|
||||
if loc.t != nil:
|
||||
if loc.lode != nil:
|
||||
add(result, '^')
|
||||
encodeVInt(cast[int32](loc.t.id), result)
|
||||
pushType(w, loc.t)
|
||||
encodeNode(w, unknownLineInfo(), loc.lode, result)
|
||||
#encodeVInt(cast[int32](loc.t.id), result)
|
||||
#pushType(w, loc.t)
|
||||
if loc.r != nil:
|
||||
add(result, '!')
|
||||
encodeStr($loc.r, result)
|
||||
@@ -579,7 +580,7 @@ proc process(c: PPassContext, n: PNode): PNode =
|
||||
for i in countup(0, sonsLen(n) - 1): discard process(c, n.sons[i])
|
||||
#var s = n.sons[namePos].sym
|
||||
#addInterfaceSym(w, s)
|
||||
of nkProcDef, nkIteratorDef, nkConverterDef,
|
||||
of nkProcDef, nkFuncDef, nkIteratorDef, nkConverterDef,
|
||||
nkTemplateDef, nkMacroDef:
|
||||
let s = n.sons[namePos].sym
|
||||
if s == nil: internalError(n.info, "rodwrite.process")
|
||||
|
||||
@@ -449,7 +449,7 @@ include semtypes, semtempl, semgnrc, semstmts, semexprs
|
||||
proc addCodeForGenerics(c: PContext, n: PNode) =
|
||||
for i in countup(c.lastGenericIdx, c.generics.len - 1):
|
||||
var prc = c.generics[i].inst.sym
|
||||
if prc.kind in {skProc, skMethod, skConverter} and prc.magic == mNone:
|
||||
if prc.kind in {skProc, skFunc, skMethod, skConverter} and prc.magic == mNone:
|
||||
if prc.ast == nil or prc.ast.sons[bodyPos] == nil:
|
||||
internalError(prc.info, "no code for " & prc.name.s)
|
||||
else:
|
||||
@@ -495,13 +495,15 @@ proc isImportSystemStmt(n: PNode): bool =
|
||||
case n.kind
|
||||
of nkImportStmt:
|
||||
for x in n:
|
||||
let f = checkModuleName(x, false)
|
||||
if x.kind == nkIdent:
|
||||
let f = checkModuleName(x, false)
|
||||
if f == magicsys.systemModule.info.fileIndex:
|
||||
return true
|
||||
of nkImportExceptStmt, nkFromStmt:
|
||||
if n[0].kind == nkIdent:
|
||||
let f = checkModuleName(n[0], false)
|
||||
if f == magicsys.systemModule.info.fileIndex:
|
||||
return true
|
||||
of nkImportExceptStmt, nkFromStmt:
|
||||
let f = checkModuleName(n[0], false)
|
||||
if f == magicsys.systemModule.info.fileIndex:
|
||||
return true
|
||||
else: discard
|
||||
|
||||
proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
|
||||
|
||||
@@ -185,7 +185,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case t.kind
|
||||
of tyNone, tyEmpty, tyVoid: discard
|
||||
of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
|
||||
tyPtr, tyString, tyRef:
|
||||
tyPtr, tyString, tyRef, tyOpt:
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tyArray, tySequence:
|
||||
if tfHasAsgn in t.flags:
|
||||
@@ -227,9 +227,9 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
tyTypeDesc, tyGenericInvocation, tyForward:
|
||||
internalError(c.info, "assignment requested for type: " & typeToString(t))
|
||||
of tyOrdinal, tyRange, tyInferred,
|
||||
tyGenericInst, tyFieldAccessor, tyStatic, tyVar, tyAlias:
|
||||
tyGenericInst, tyStatic, tyVar, tyAlias:
|
||||
liftBodyAux(c, lastSon(t), body, x, y)
|
||||
of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("liftBodyAux")
|
||||
of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("liftBodyAux")
|
||||
|
||||
proc newProcType(info: TLineInfo; owner: PSym): PType =
|
||||
result = newType(tyProc, owner)
|
||||
|
||||
@@ -468,7 +468,7 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||
for i in countup(0, len(a)-1):
|
||||
var candidate = a.sons[i].sym
|
||||
if candidate.kind in {skProc, skMethod, skConverter,
|
||||
skIterator}:
|
||||
skFunc, skIterator}:
|
||||
# it suffices that the candidate has the proper number of generic
|
||||
# type parameters:
|
||||
if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1:
|
||||
|
||||
@@ -65,7 +65,7 @@ type
|
||||
efWantStmt, efAllowStmt, efDetermineType, efExplain,
|
||||
efAllowDestructor, efWantValue, efOperand, efNoSemCheck,
|
||||
efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo,
|
||||
|
||||
|
||||
TExprFlags* = set[TExprFlag]
|
||||
|
||||
TTypeAttachedOp* = enum
|
||||
@@ -112,6 +112,7 @@ type
|
||||
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable,
|
||||
info: TLineInfo): PSym
|
||||
includedFiles*: IntSet # used to detect recursive include files
|
||||
pureEnumFields*: TStrTable # pure enum fields that can be used unambiguously
|
||||
userPragmas*: TStrTable
|
||||
evalContext*: PEvalContext
|
||||
unknownIdents*: IntSet # ids of all unknown identifiers to prevent
|
||||
@@ -210,6 +211,7 @@ proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext
|
||||
result.converters = @[]
|
||||
result.patterns = @[]
|
||||
result.includedFiles = initIntSet()
|
||||
initStrTable(result.pureEnumFields)
|
||||
initStrTable(result.userPragmas)
|
||||
result.generics = @[]
|
||||
result.unknownIdents = initIntSet()
|
||||
@@ -371,7 +373,7 @@ proc makeRangeType*(c: PContext; first, last: BiggestInt;
|
||||
addSonSkipIntLit(result, intType) # basetype of range
|
||||
|
||||
proc markIndirect*(c: PContext, s: PSym) {.inline.} =
|
||||
if s.kind in {skProc, skConverter, skMethod, skIterator}:
|
||||
if s.kind in {skProc, skFunc, skConverter, skMethod, skIterator}:
|
||||
incl(s.flags, sfAddrTaken)
|
||||
# XXX add to 'c' for global analysis
|
||||
|
||||
|
||||
@@ -246,8 +246,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
|
||||
localError(n.info, errXExpectsTypeOrValue, opToStr[m])
|
||||
else:
|
||||
n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType})
|
||||
var typ = skipTypes(n.sons[1].typ, abstractVarRange +
|
||||
{tyTypeDesc, tyFieldAccessor})
|
||||
var typ = skipTypes(n.sons[1].typ, abstractVarRange + {tyTypeDesc})
|
||||
case typ.kind
|
||||
of tySequence, tyString, tyCString, tyOpenArray, tyVarargs:
|
||||
n.typ = getSysType(tyInt)
|
||||
@@ -255,7 +254,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
|
||||
n.typ = typ.sons[0] # indextype
|
||||
of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32:
|
||||
# do not skip the range!
|
||||
n.typ = n.sons[1].typ.skipTypes(abstractVar + {tyFieldAccessor})
|
||||
n.typ = n.sons[1].typ.skipTypes(abstractVar)
|
||||
of tyGenericParam:
|
||||
# prepare this for resolving in semtypinst:
|
||||
# we must use copyTree here in order to avoid creating a cycle
|
||||
@@ -279,7 +278,7 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
|
||||
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
|
||||
|
||||
let t1 = n[1].typ.skipTypes({tyTypeDesc, tyFieldAccessor})
|
||||
let t1 = n[1].typ.skipTypes({tyTypeDesc})
|
||||
|
||||
if n[2].kind in {nkStrLit..nkTripleStrLit}:
|
||||
case n[2].strVal.normalize
|
||||
@@ -573,7 +572,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||
optImplicitStatic notin gOptions: return
|
||||
|
||||
if callee.magic notin ctfeWhitelist: return
|
||||
if callee.kind notin {skProc, skConverter} or callee.isGenericRoutine:
|
||||
if callee.kind notin {skProc, skFunc, skConverter} or callee.isGenericRoutine:
|
||||
return
|
||||
|
||||
if n.typ != nil and typeAllowed(n.typ, skConst) != nil: return
|
||||
@@ -615,10 +614,10 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
|
||||
# for typeof support.
|
||||
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
|
||||
result = semOverloadedCall(c, n, nOrig,
|
||||
{skProc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags)
|
||||
{skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags)
|
||||
else:
|
||||
result = semOverloadedCall(c, n, nOrig,
|
||||
{skProc, skMethod, skConverter, skMacro, skTemplate}, flags)
|
||||
{skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags)
|
||||
|
||||
if result != nil:
|
||||
if result.sons[0].kind != nkSym:
|
||||
@@ -1086,9 +1085,6 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
if field != nil:
|
||||
n.typ = makeTypeDesc(c, field.typ)
|
||||
return n
|
||||
#n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.typ])
|
||||
#n.typ.n = copyTree(n)
|
||||
#return n
|
||||
else:
|
||||
tryReadingGenericParam(ty)
|
||||
return
|
||||
@@ -1229,7 +1225,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
else: nil
|
||||
if s != nil:
|
||||
case s.kind
|
||||
of skProc, skMethod, skConverter, skIterator:
|
||||
of skProc, skFunc, skMethod, skConverter, skIterator:
|
||||
# type parameters: partial generic specialization
|
||||
n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
|
||||
result = explicitGenericInstantiation(c, n, s)
|
||||
@@ -1397,7 +1393,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
||||
proc semReturn(c: PContext, n: PNode): PNode =
|
||||
result = n
|
||||
checkSonsLen(n, 1)
|
||||
if c.p.owner.kind in {skConverter, skMethod, skProc, skMacro} or (
|
||||
if c.p.owner.kind in {skConverter, skMethod, skProc, skFunc, skMacro} or (
|
||||
c.p.owner.kind == skIterator and c.p.owner.typ.callConv == ccClosure):
|
||||
if n.sons[0].kind != nkEmpty:
|
||||
# transform ``return expr`` to ``result = expr; return``
|
||||
@@ -1620,9 +1616,10 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
|
||||
# Preserve the magic symbol in order to be handled in evals.nim
|
||||
internalAssert n.sons[0].sym.magic == mExpandToAst
|
||||
#n.typ = getSysSym("NimNode").typ # expandedSym.getReturnType
|
||||
n.typ = if getCompilerProc("NimNode") != nil: sysTypeFromName"NimNode"
|
||||
else: sysTypeFromName"PNimrodNode"
|
||||
result = n
|
||||
if n.kind == nkStmtList and n.len == 1: result = n[0]
|
||||
else: result = n
|
||||
result.typ = if getCompilerProc("NimNode") != nil: sysTypeFromName"NimNode"
|
||||
else: sysTypeFromName"PNimrodNode"
|
||||
|
||||
proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
|
||||
flags: TExprFlags = {}): PNode =
|
||||
@@ -2114,12 +2111,14 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
if nfSem in n.flags: return
|
||||
case n.kind
|
||||
of nkIdent, nkAccQuoted:
|
||||
let checks = if efNoEvaluateGeneric in flags: {checkUndeclared}
|
||||
else: {checkUndeclared, checkModule, checkAmbiguity}
|
||||
let checks = if efNoEvaluateGeneric in flags:
|
||||
{checkUndeclared, checkPureEnumFields}
|
||||
else:
|
||||
{checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields}
|
||||
var s = qualifiedLookUp(c, n, checks)
|
||||
if c.matchedConcept == nil: semCaptureSym(s, c.p.owner)
|
||||
result = semSym(c, n, s, flags)
|
||||
if s.kind in {skProc, skMethod, skConverter, skIterator}:
|
||||
if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator}:
|
||||
#performProcvarCheck(c, n, s)
|
||||
result = symChoice(c, n, s, scClosed)
|
||||
if result.kind == nkSym:
|
||||
@@ -2214,7 +2213,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
errorUseQualifier(c, n.info, s)
|
||||
elif s.magic == mNone: result = semDirectOp(c, n, flags)
|
||||
else: result = semMagic(c, n, s, flags)
|
||||
of skProc, skMethod, skConverter, skIterator:
|
||||
of skProc, skFunc, skMethod, skConverter, skIterator:
|
||||
if s.magic == mNone: result = semDirectOp(c, n, flags)
|
||||
else: result = semMagic(c, n, s, flags)
|
||||
else:
|
||||
@@ -2328,6 +2327,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
of nkPragma: pragma(c, c.p.owner, n, stmtPragmas)
|
||||
of nkIteratorDef: result = semIterator(c, n)
|
||||
of nkProcDef: result = semProc(c, n)
|
||||
of nkFuncDef: result = semFunc(c, n)
|
||||
of nkMethodDef: result = semMethod(c, n)
|
||||
of nkConverterDef: result = semConverterDef(c, n)
|
||||
of nkMacroDef: result = semMacroDef(c, n)
|
||||
|
||||
@@ -486,6 +486,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||
of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n)
|
||||
of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n)
|
||||
of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLowerAscii, n)
|
||||
of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[platform.hostOS].name), n)
|
||||
of mBuildCPU: result = newStrNodeT(platform.CPU[platform.hostCPU].name.toLowerAscii, n)
|
||||
of mAppType: result = getAppType(n)
|
||||
of mNaN: result = newFloatNodeT(NaN, n)
|
||||
of mInf: result = newFloatNodeT(Inf, n)
|
||||
@@ -498,7 +500,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||
result = newStrNodeT(lookupSymbol(s.name), n)
|
||||
else:
|
||||
result = copyTree(s.ast)
|
||||
of {skProc, skMethod}:
|
||||
of {skProc, skFunc, skMethod}:
|
||||
result = n
|
||||
of skType:
|
||||
# XXX gensym'ed symbols can come here and cannot be resolved. This is
|
||||
@@ -522,7 +524,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||
of nkCallKinds:
|
||||
if n.sons[0].kind != nkSym: return
|
||||
var s = n.sons[0].sym
|
||||
if s.kind != skProc: return
|
||||
if s.kind != skProc and s.kind != skFunc: return
|
||||
try:
|
||||
case s.magic
|
||||
of mNone:
|
||||
|
||||
@@ -61,7 +61,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
of skUnknown:
|
||||
# Introduced in this pass! Leave it as an identifier.
|
||||
result = n
|
||||
of skProc, skMethod, skIterator, skConverter, skModule:
|
||||
of skProc, skFunc, skMethod, skIterator, skConverter, skModule:
|
||||
result = symChoice(c, n, s, scOpen)
|
||||
of skTemplate:
|
||||
if macroToExpandSym(s):
|
||||
@@ -105,6 +105,10 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
result = n
|
||||
let ident = considerQuotedIdent(n)
|
||||
var s = searchInScopes(c, ident).skipAlias(n)
|
||||
if s == nil:
|
||||
s = strTableGet(c.pureEnumFields, ident)
|
||||
if s != nil and contains(c.ambiguousSymbols, s.id):
|
||||
s = nil
|
||||
if s == nil:
|
||||
if ident.id notin ctx.toMixin and withinMixin notin flags:
|
||||
errorUndeclaredIdentifier(c, n.info, ident.s)
|
||||
@@ -239,7 +243,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
of skUnknown, skParam:
|
||||
# Leave it as an identifier.
|
||||
discard
|
||||
of skProc, skMethod, skIterator, skConverter, skModule:
|
||||
of skProc, skFunc, skMethod, skIterator, skConverter, skModule:
|
||||
result.sons[0] = sc
|
||||
# do not check of 's.magic==mRoof' here because it might be some
|
||||
# other '^' but after overload resolution the proper one:
|
||||
@@ -433,7 +437,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
for j in countup(0, L-3):
|
||||
addTempDecl(c, getIdentNode(a.sons[j]), skParam)
|
||||
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
|
||||
nkIteratorDef, nkLambdaKinds:
|
||||
nkFuncDef, nkIteratorDef, nkLambdaKinds:
|
||||
checkSonsLen(n, bodyPos + 1)
|
||||
if n.sons[namePos].kind != nkEmpty:
|
||||
addTempDecl(c, getIdentNode(n.sons[0]), skProc)
|
||||
|
||||
@@ -36,7 +36,7 @@ proc rawPushProcCon(c: PContext, owner: PSym) =
|
||||
c.p = x
|
||||
|
||||
proc rawHandleSelf(c: PContext; owner: PSym) =
|
||||
const callableSymbols = {skProc, skMethod, skConverter, skIterator, skMacro}
|
||||
const callableSymbols = {skProc, skFunc, skMethod, skConverter, skIterator, skMacro}
|
||||
if c.selfName != nil and owner.kind in callableSymbols and owner.typ != nil:
|
||||
let params = owner.typ.n
|
||||
if params.len > 1:
|
||||
|
||||
@@ -387,7 +387,7 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
|
||||
addFactNeg(c.guards, canon(n.sons[0]))
|
||||
dec c.inLoop
|
||||
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
|
||||
nkMacroDef, nkTemplateDef, nkConstSection, nkPragma:
|
||||
nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef:
|
||||
discard
|
||||
else:
|
||||
analyseSons(c, n)
|
||||
|
||||
@@ -835,7 +835,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
setLen(tracked.locked, oldLocked)
|
||||
tracked.currLockLevel = oldLockLevel
|
||||
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
|
||||
nkMacroDef, nkTemplateDef, nkLambda, nkDo:
|
||||
nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
|
||||
discard
|
||||
of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
if n.len == 2: track(tracked, n.sons[1])
|
||||
@@ -937,7 +937,7 @@ proc trackProc*(s: PSym, body: PNode) =
|
||||
track(t, body)
|
||||
if not isEmptyType(s.typ.sons[0]) and
|
||||
{tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} and
|
||||
s.kind in {skProc, skConverter, skMethod}:
|
||||
s.kind in {skProc, skFunc, skConverter, skMethod}:
|
||||
var res = s.ast.sons[resultPos].sym # get result symbol
|
||||
if res.id notin t.init:
|
||||
message(body.info, warnProveInit, "result")
|
||||
@@ -979,10 +979,10 @@ proc trackProc*(s: PSym, body: PNode) =
|
||||
message(s.info, warnLockLevel,
|
||||
"declared lock level is $1, but real lock level is $2" %
|
||||
[$s.typ.lockLevel, $t.maxLockLevel])
|
||||
when useWriteTracking: trackWrites(s, body)
|
||||
if s.kind == skFunc: trackWrites(s, body)
|
||||
|
||||
proc trackTopLevelStmt*(module: PSym; n: PNode) =
|
||||
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef,
|
||||
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
|
||||
nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
|
||||
return
|
||||
var effects = newNode(nkEffectList, n.info)
|
||||
|
||||
@@ -437,8 +437,6 @@ proc isDiscardUnderscore(v: PSym): bool =
|
||||
proc semUsing(c: PContext; n: PNode): PNode =
|
||||
result = ast.emptyNode
|
||||
if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "using")
|
||||
if not experimentalMode(c):
|
||||
localError(n.info, "use the {.experimental.} pragma to enable 'using'")
|
||||
for i in countup(0, sonsLen(n)-1):
|
||||
var a = n.sons[i]
|
||||
if gCmd == cmdIdeTools: suggestStmt(c, a)
|
||||
@@ -493,6 +491,7 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
|
||||
addSon(obj.n, newSymNode(field))
|
||||
n.sons[0] = makeDeref x
|
||||
n.sons[1] = newSymNode(field)
|
||||
n.typ = field.typ
|
||||
else:
|
||||
localError(n.info, "implicit object field construction " &
|
||||
"requires a .partial object, but got " & typeToString(obj))
|
||||
@@ -1233,7 +1232,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
|
||||
s.typ = n.typ
|
||||
for i in 1..<params.len:
|
||||
if params[i].typ.kind in {tyTypeDesc, tyGenericParam,
|
||||
tyFromExpr, tyFieldAccessor}+tyTypeClasses:
|
||||
tyFromExpr}+tyTypeClasses:
|
||||
localError(params[i].info, "cannot infer type of parameter: " &
|
||||
params[i].sym.name.s)
|
||||
#params[i].sym.owner = s
|
||||
@@ -1603,6 +1602,9 @@ proc semIterator(c: PContext, n: PNode): PNode =
|
||||
proc semProc(c: PContext, n: PNode): PNode =
|
||||
result = semProcAux(c, n, skProc, procPragmas)
|
||||
|
||||
proc semFunc(c: PContext, n: PNode): PNode =
|
||||
result = semProcAux(c, n, skFunc, procPragmas)
|
||||
|
||||
proc semMethod(c: PContext, n: PNode): PNode =
|
||||
if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "method")
|
||||
result = semProcAux(c, n, skMethod, methodPragmas)
|
||||
|
||||
@@ -449,6 +449,8 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
a.sons[2] = semTemplBody(c, a.sons[2])
|
||||
of nkProcDef, nkLambdaKinds:
|
||||
result = semRoutineInTemplBody(c, n, skProc)
|
||||
of nkFuncDef:
|
||||
result = semRoutineInTemplBody(c, n, skFunc)
|
||||
of nkMethodDef:
|
||||
result = semRoutineInTemplBody(c, n, skMethod)
|
||||
of nkIteratorDef:
|
||||
|
||||
@@ -88,7 +88,9 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
if not isPure: strTableAdd(c.module.tab, e)
|
||||
addSon(result.n, newSymNode(e))
|
||||
styleCheckDef(e)
|
||||
if sfGenSym notin e.flags and not isPure: addDecl(c, e)
|
||||
if sfGenSym notin e.flags:
|
||||
if not isPure: addDecl(c, e)
|
||||
else: importPureEnumField(c, e)
|
||||
if isPure and strTableIncl(symbols, e):
|
||||
wrongRedefinition(e.info, e.name.s)
|
||||
inc(counter)
|
||||
@@ -1391,6 +1393,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
of mSet: result = semSet(c, n, prev)
|
||||
of mOrdinal: result = semOrdinal(c, n, prev)
|
||||
of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
|
||||
of mOpt: result = semContainer(c, n, tyOpt, "opt", prev)
|
||||
of mVarargs: result = semVarargs(c, n, prev)
|
||||
of mTypeDesc: result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
|
||||
of mExpr:
|
||||
@@ -1590,6 +1593,8 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||
setMagicType(m, tySet, 0)
|
||||
of mSeq:
|
||||
setMagicType(m, tySequence, 0)
|
||||
of mOpt:
|
||||
setMagicType(m, tyOpt, 0)
|
||||
of mOrdinal:
|
||||
setMagicType(m, tyOrdinal, 0)
|
||||
rawAddSon(m.typ, newTypeS(tyNone, c))
|
||||
|
||||
@@ -170,7 +170,7 @@ proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
|
||||
if isTypeParam(n[i]): needsFixing = true
|
||||
if needsFixing:
|
||||
n.sons[0] = newSymNode(n.sons[0].sym.owner)
|
||||
return cl.c.semOverloadedCall(cl.c, n, n, {skProc}, {})
|
||||
return cl.c.semOverloadedCall(cl.c, n, n, {skProc, skFunc}, {})
|
||||
|
||||
for i in 0 .. <n.safeLen:
|
||||
n.sons[i] = reResolveCallsWithTypedescParams(cl, n[i])
|
||||
|
||||
@@ -201,7 +201,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
|
||||
of tyRef, tyPtr, tyGenericBody, tyVar:
|
||||
c.hashType t.lastSon, flags
|
||||
if tfVarIsPtr in t.flags: c &= ".varisptr"
|
||||
of tyFromExpr, tyFieldAccessor:
|
||||
of tyFromExpr:
|
||||
c.hashTree(t.n)
|
||||
of tyTuple:
|
||||
if t.n != nil and CoType notin flags:
|
||||
|
||||
@@ -936,7 +936,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
|
||||
tfExplicit notin aOrig.flags
|
||||
|
||||
aOrig = if useTypeLoweringRuleInTypeClass:
|
||||
aOrig.skipTypes({tyTypeDesc, tyFieldAccessor})
|
||||
aOrig.skipTypes({tyTypeDesc})
|
||||
else:
|
||||
aOrig
|
||||
|
||||
@@ -1373,7 +1373,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
|
||||
# XXX: This is very hacky. It should be moved back into liftTypeParam
|
||||
if x.kind in {tyGenericInst, tyArray} and
|
||||
c.calleeSym != nil and
|
||||
c.calleeSym.kind == skProc:
|
||||
c.calleeSym.kind in {skProc, skFunc}:
|
||||
let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f)
|
||||
return typeRel(c, inst, a)
|
||||
|
||||
@@ -1832,7 +1832,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
bothMetaCounter < 100:
|
||||
lastBindingsLength = m.bindings.counter
|
||||
inc(bothMetaCounter)
|
||||
if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
|
||||
if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds:
|
||||
result = c.semInferredLambda(c, m.bindings, arg)
|
||||
elif arg.kind != nkSym:
|
||||
return nil
|
||||
@@ -1865,7 +1865,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
else:
|
||||
result = implicitConv(nkHiddenStdConv, f, arg, m, c)
|
||||
of isInferred, isInferredConvertible:
|
||||
if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
|
||||
if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds:
|
||||
result = c.semInferredLambda(c, m.bindings, arg)
|
||||
elif arg.kind != nkSym:
|
||||
return nil
|
||||
@@ -1952,7 +1952,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
|
||||
z.calleeSym = m.calleeSym
|
||||
var best = -1
|
||||
for i in countup(0, sonsLen(arg) - 1):
|
||||
if arg.sons[i].sym.kind in {skProc, skMethod, skConverter, skIterator}:
|
||||
if arg.sons[i].sym.kind in {skProc, skFunc, skMethod, skConverter, skIterator}:
|
||||
copyCandidate(z, m)
|
||||
z.callee = arg.sons[i].typ
|
||||
if tfUnresolved in z.callee.flags: continue
|
||||
|
||||
@@ -121,7 +121,7 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
|
||||
if s.kind == skIterator:
|
||||
if c.tooEarly: return n
|
||||
else: return liftIterSym(n, getCurrOwner(c))
|
||||
elif s.kind in {skProc, skConverter, skMethod} and not c.tooEarly:
|
||||
elif s.kind in {skProc, skFunc, skConverter, skMethod} and not c.tooEarly:
|
||||
# top level .closure procs are still somewhat supported for 'Nake':
|
||||
return makeClosure(s, nil, n.info)
|
||||
#elif n.sym.kind in {skVar, skLet} and n.sym.typ.callConv == ccClosure:
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
import
|
||||
intsets, ast, astalgo, trees, msgs, strutils, platform, renderer
|
||||
|
||||
proc firstOrd*(t: PType): BiggestInt
|
||||
proc lastOrd*(t: PType): BiggestInt
|
||||
proc lengthOrd*(t: PType): BiggestInt
|
||||
type
|
||||
TPreferedDesc* = enum
|
||||
preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg
|
||||
@@ -48,8 +45,6 @@ type
|
||||
|
||||
proc equalParams*(a, b: PNode): TParamsEquality
|
||||
# returns whether the parameter lists of the procs a, b are exactly the same
|
||||
proc isOrdinalType*(t: PType): bool
|
||||
proc enumHasHoles*(t: PType): bool
|
||||
|
||||
const
|
||||
# TODO: Remove tyTypeDesc from each abstractX and (where necessary)
|
||||
@@ -70,17 +65,6 @@ const
|
||||
typedescPtrs* = abstractPtrs + {tyTypeDesc}
|
||||
typedescInst* = abstractInst + {tyTypeDesc}
|
||||
|
||||
proc containsObject*(t: PType): bool
|
||||
proc containsGarbageCollectedRef*(typ: PType): bool
|
||||
proc containsHiddenPointer*(typ: PType): bool
|
||||
proc canFormAcycle*(typ: PType): bool
|
||||
proc isCompatibleToCString*(a: PType): bool
|
||||
proc getOrdValue*(n: PNode): BiggestInt
|
||||
proc computeSize*(typ: PType): BiggestInt
|
||||
proc getSize*(typ: PType): BiggestInt
|
||||
proc isPureObject*(typ: PType): bool
|
||||
proc invalidGenericInst*(f: PType): bool
|
||||
# for debugging
|
||||
type
|
||||
TTypeFieldResult* = enum
|
||||
frNone, # type has no object type field
|
||||
@@ -92,16 +76,16 @@ proc analyseObjectWithTypeField*(t: PType): TTypeFieldResult
|
||||
# made or intializing of the type field suffices or if there is no type field
|
||||
# at all in this type.
|
||||
|
||||
proc invalidGenericInst(f: PType): bool =
|
||||
proc invalidGenericInst*(f: PType): bool =
|
||||
result = f.kind == tyGenericInst and lastSon(f) == nil
|
||||
|
||||
proc isPureObject(typ: PType): bool =
|
||||
proc isPureObject*(typ: PType): bool =
|
||||
var t = typ
|
||||
while t.kind == tyObject and t.sons[0] != nil:
|
||||
t = t.sons[0].skipTypes(skipPtrs)
|
||||
result = t.sym != nil and sfPure in t.sym.flags
|
||||
|
||||
proc getOrdValue(n: PNode): BiggestInt =
|
||||
proc getOrdValue*(n: PNode): BiggestInt =
|
||||
case n.kind
|
||||
of nkCharLit..nkUInt64Lit: result = n.intVal
|
||||
of nkNilLit: result = 0
|
||||
@@ -116,14 +100,6 @@ proc isIntLit*(t: PType): bool {.inline.} =
|
||||
proc isFloatLit*(t: PType): bool {.inline.} =
|
||||
result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit
|
||||
|
||||
proc isCompatibleToCString(a: PType): bool =
|
||||
if a.kind == tyArray:
|
||||
if (firstOrd(a.sons[0]) == 0) and
|
||||
(skipTypes(a.sons[0], {tyRange, tyGenericInst, tyAlias}).kind in
|
||||
{tyInt..tyInt64, tyUInt..tyUInt64}) and
|
||||
(a.sons[1].kind == tyChar):
|
||||
result = true
|
||||
|
||||
proc getProcHeader*(sym: PSym; prefer: TPreferedDesc = preferName): string =
|
||||
result = sym.owner.name.s & '.' & sym.name.s & '('
|
||||
var n = sym.typ.n
|
||||
@@ -151,7 +127,7 @@ proc elemType*(t: PType): PType =
|
||||
else: result = t.lastSon
|
||||
assert(result != nil)
|
||||
|
||||
proc isOrdinalType(t: PType): bool =
|
||||
proc isOrdinalType*(t: PType): bool =
|
||||
assert(t != nil)
|
||||
const
|
||||
# caution: uint, uint64 are no ordinal types!
|
||||
@@ -159,7 +135,7 @@ proc isOrdinalType(t: PType): bool =
|
||||
parentKinds = {tyRange, tyOrdinal, tyGenericInst, tyAlias, tyDistinct}
|
||||
t.kind in baseKinds or (t.kind in parentKinds and isOrdinalType(t.sons[0]))
|
||||
|
||||
proc enumHasHoles(t: PType): bool =
|
||||
proc enumHasHoles*(t: PType): bool =
|
||||
var b = t
|
||||
while b.kind in {tyRange, tyGenericInst, tyAlias}: b = b.sons[0]
|
||||
result = b.kind == tyEnum and tfEnumHasHoles in b.flags
|
||||
@@ -252,7 +228,7 @@ proc searchTypeFor(t: PType, predicate: TTypePredicate): bool =
|
||||
proc isObjectPredicate(t: PType): bool =
|
||||
result = t.kind == tyObject
|
||||
|
||||
proc containsObject(t: PType): bool =
|
||||
proc containsObject*(t: PType): bool =
|
||||
result = searchTypeFor(t, isObjectPredicate)
|
||||
|
||||
proc isObjectWithTypeFieldPredicate(t: PType): bool =
|
||||
@@ -297,7 +273,7 @@ proc isGCRef(t: PType): bool =
|
||||
result = t.kind in GcTypeKinds or
|
||||
(t.kind == tyProc and t.callConv == ccClosure)
|
||||
|
||||
proc containsGarbageCollectedRef(typ: PType): bool =
|
||||
proc containsGarbageCollectedRef*(typ: PType): bool =
|
||||
# returns true if typ contains a reference, sequence or string (all the
|
||||
# things that are garbage-collected)
|
||||
result = searchTypeFor(typ, isGCRef)
|
||||
@@ -312,7 +288,7 @@ proc containsTyRef*(typ: PType): bool =
|
||||
proc isHiddenPointer(t: PType): bool =
|
||||
result = t.kind in {tyString, tySequence}
|
||||
|
||||
proc containsHiddenPointer(typ: PType): bool =
|
||||
proc containsHiddenPointer*(typ: PType): bool =
|
||||
# returns true if typ contains a string, table or sequence (all the things
|
||||
# that need to be copied deeply)
|
||||
result = searchTypeFor(typ, isHiddenPointer)
|
||||
@@ -355,7 +331,7 @@ proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool =
|
||||
of tyProc: result = typ.callConv == ccClosure
|
||||
else: discard
|
||||
|
||||
proc canFormAcycle(typ: PType): bool =
|
||||
proc canFormAcycle*(typ: PType): bool =
|
||||
var marker = initIntSet()
|
||||
result = canFormAcycleAux(marker, typ, typ.id)
|
||||
|
||||
@@ -521,7 +497,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
of tyExpr:
|
||||
internalAssert t.len == 0
|
||||
result = "untyped"
|
||||
of tyFromExpr, tyFieldAccessor:
|
||||
of tyFromExpr:
|
||||
result = renderTree(t.n)
|
||||
of tyArray:
|
||||
if t.sons[0].kind == tyRange:
|
||||
@@ -532,6 +508,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
typeToString(t.sons[1]) & ']'
|
||||
of tySequence:
|
||||
result = "seq[" & typeToString(t.sons[0]) & ']'
|
||||
of tyOpt:
|
||||
result = "opt[" & typeToString(t.sons[0]) & ']'
|
||||
of tyOrdinal:
|
||||
result = "ordinal[" & typeToString(t.sons[0]) & ']'
|
||||
of tySet:
|
||||
@@ -605,7 +583,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
result = typeToStr[t.kind]
|
||||
result.addTypeFlags(t)
|
||||
|
||||
proc firstOrd(t: PType): BiggestInt =
|
||||
proc firstOrd*(t: PType): BiggestInt =
|
||||
case t.kind
|
||||
of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy:
|
||||
result = 0
|
||||
@@ -630,7 +608,7 @@ proc firstOrd(t: PType): BiggestInt =
|
||||
else:
|
||||
assert(t.n.sons[0].kind == nkSym)
|
||||
result = t.n.sons[0].sym.position
|
||||
of tyGenericInst, tyDistinct, tyTypeDesc, tyFieldAccessor, tyAlias:
|
||||
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias:
|
||||
result = firstOrd(lastSon(t))
|
||||
of tyOrdinal:
|
||||
if t.len > 0: result = firstOrd(lastSon(t))
|
||||
@@ -639,7 +617,7 @@ proc firstOrd(t: PType): BiggestInt =
|
||||
internalError("invalid kind for first(" & $t.kind & ')')
|
||||
result = 0
|
||||
|
||||
proc lastOrd(t: PType): BiggestInt =
|
||||
proc lastOrd*(t: PType): BiggestInt =
|
||||
case t.kind
|
||||
of tyBool: result = 1
|
||||
of tyChar: result = 255
|
||||
@@ -666,7 +644,7 @@ proc lastOrd(t: PType): BiggestInt =
|
||||
of tyEnum:
|
||||
assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
|
||||
result = t.n.sons[sonsLen(t.n) - 1].sym.position
|
||||
of tyGenericInst, tyDistinct, tyTypeDesc, tyFieldAccessor, tyAlias:
|
||||
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias:
|
||||
result = lastOrd(lastSon(t))
|
||||
of tyProxy: result = 0
|
||||
of tyOrdinal:
|
||||
@@ -676,7 +654,7 @@ proc lastOrd(t: PType): BiggestInt =
|
||||
internalError("invalid kind for last(" & $t.kind & ')')
|
||||
result = 0
|
||||
|
||||
proc lengthOrd(t: PType): BiggestInt =
|
||||
proc lengthOrd*(t: PType): BiggestInt =
|
||||
case t.kind
|
||||
of tyInt64, tyInt32, tyInt: result = lastOrd(t)
|
||||
of tyDistinct: result = lengthOrd(t.sons[0])
|
||||
@@ -689,6 +667,14 @@ proc lengthOrd(t: PType): BiggestInt =
|
||||
else:
|
||||
result = lastOrd(t) - firstOrd(t) + 1
|
||||
|
||||
proc isCompatibleToCString*(a: PType): bool =
|
||||
if a.kind == tyArray:
|
||||
if (firstOrd(a.sons[0]) == 0) and
|
||||
(skipTypes(a.sons[0], {tyRange, tyGenericInst, tyAlias}).kind in
|
||||
{tyInt..tyInt64, tyUInt..tyUInt64}) and
|
||||
(a.sons[1].kind == tyChar):
|
||||
result = true
|
||||
|
||||
# -------------- type equality -----------------------------------------------
|
||||
|
||||
type
|
||||
@@ -989,7 +975,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
result = a.sym.position == b.sym.position
|
||||
of tyGenericInvocation, tyGenericBody, tySequence,
|
||||
tyOpenArray, tySet, tyRef, tyPtr, tyVar,
|
||||
tyArray, tyProc, tyVarargs, tyOrdinal, tyTypeClasses, tyFieldAccessor:
|
||||
tyArray, tyProc, tyVarargs, tyOrdinal, tyTypeClasses, tyOpt:
|
||||
cycleCheck()
|
||||
if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n
|
||||
result = sameChildrenAux(a, b, c) and sameFlags(a, b)
|
||||
@@ -1007,7 +993,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
cycleCheck()
|
||||
result = sameTypeAux(a.lastSon, b.lastSon, c)
|
||||
of tyNone: result = false
|
||||
of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("sameFlags")
|
||||
of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("sameFlags")
|
||||
|
||||
proc sameBackendType*(x, y: PType): bool =
|
||||
var c = initSameTypeClosure()
|
||||
@@ -1089,7 +1075,7 @@ proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind,
|
||||
of nkNone..nkNilLit:
|
||||
discard
|
||||
else:
|
||||
if n.kind == nkRecCase and kind in {skProc, skConst}:
|
||||
if n.kind == nkRecCase and kind in {skProc, skFunc, skConst}:
|
||||
return n[0].typ
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
let it = n.sons[i]
|
||||
@@ -1107,7 +1093,7 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
|
||||
|
||||
proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
flags: TTypeAllowedFlags = {}): PType =
|
||||
assert(kind in {skVar, skLet, skConst, skProc, skParam, skResult})
|
||||
assert(kind in {skVar, skLet, skConst, skProc, skFunc, skParam, skResult})
|
||||
# if we have already checked the type, return true, because we stop the
|
||||
# evaluation if something is wrong:
|
||||
result = nil
|
||||
@@ -1116,7 +1102,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
var t = skipTypes(typ, abstractInst-{tyTypeDesc})
|
||||
case t.kind
|
||||
of tyVar:
|
||||
if kind in {skProc, skConst}: return t
|
||||
if kind in {skProc, skFunc, skConst}: return t
|
||||
var t2 = skipTypes(t.sons[0], abstractInst-{tyTypeDesc})
|
||||
case t2.kind
|
||||
of tyVar:
|
||||
@@ -1144,7 +1130,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
of tyTypeClasses:
|
||||
if not (tfGenericTypeParam in t.flags or taField notin flags): result = t
|
||||
of tyGenericBody, tyGenericParam, tyGenericInvocation,
|
||||
tyNone, tyForward, tyFromExpr, tyFieldAccessor:
|
||||
tyNone, tyForward, tyFromExpr:
|
||||
result = t
|
||||
of tyNil:
|
||||
if kind != skConst: result = t
|
||||
@@ -1160,7 +1146,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
of tyOpenArray, tyVarargs:
|
||||
if kind != skParam: result = t
|
||||
else: result = typeAllowedAux(marker, t.sons[0], skVar, flags)
|
||||
of tySequence:
|
||||
of tySequence, tyOpt:
|
||||
if t.sons[0].kind != tyEmpty:
|
||||
result = typeAllowedAux(marker, t.sons[0], skVar, flags+{taHeap})
|
||||
of tyArray:
|
||||
@@ -1176,7 +1162,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
result = typeAllowedAux(marker, t.sons[i], kind, flags)
|
||||
if result != nil: break
|
||||
of tyObject, tyTuple:
|
||||
if kind in {skProc, skConst} and
|
||||
if kind in {skProc, skFunc, skConst} and
|
||||
t.kind == tyObject and t.sons[0] != nil: return t
|
||||
let flags = flags+{taField}
|
||||
for i in countup(0, sonsLen(t) - 1):
|
||||
@@ -1188,7 +1174,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
# for now same as error node; we say it's a valid type as it should
|
||||
# prevent cascading errors:
|
||||
result = nil
|
||||
of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("typeAllowedAux")
|
||||
of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("typeAllowedAux")
|
||||
|
||||
proc typeAllowed*(t: PType, kind: TSymKind): PType =
|
||||
# returns 'nil' on success and otherwise the part of the type that is
|
||||
@@ -1199,6 +1185,63 @@ proc typeAllowed*(t: PType, kind: TSymKind): PType =
|
||||
proc align(address, alignment: BiggestInt): BiggestInt =
|
||||
result = (address + (alignment - 1)) and not (alignment - 1)
|
||||
|
||||
type
|
||||
OptKind* = enum ## What to map 'opt T' to internally.
|
||||
oBool ## opt[T] requires an additional 'bool' field
|
||||
oNil ## opt[T] has no overhead since 'nil'
|
||||
## is available
|
||||
oEnum ## We can use some enum value that is not yet
|
||||
## used for opt[T]
|
||||
oPtr ## opt[T] actually introduces a hidden pointer
|
||||
## in order for the type recursion to work
|
||||
|
||||
proc optKind*(typ: PType): OptKind =
|
||||
## return true iff 'opt[T]' can be mapped to 'T' internally
|
||||
## because we have a 'nil' value available:
|
||||
assert typ.kind == tyOpt
|
||||
case typ.sons[0].skipTypes(abstractInst).kind
|
||||
of tyRef, tyPtr, tyProc:
|
||||
result = oNil
|
||||
of tyArray, tyObject, tyTuple:
|
||||
result = oPtr
|
||||
of tyBool: result = oEnum
|
||||
of tyEnum:
|
||||
assert(typ.n.sons[0].kind == nkSym)
|
||||
if typ.n.sons[0].sym.position != low(int):
|
||||
result = oEnum
|
||||
else:
|
||||
result = oBool
|
||||
else:
|
||||
result = oBool
|
||||
|
||||
proc optLowering*(typ: PType): PType =
|
||||
case optKind(typ)
|
||||
of oNil: result = typ.sons[0]
|
||||
of oPtr:
|
||||
result = newType(tyOptAsRef, typ.owner)
|
||||
result.rawAddSon typ.sons[0]
|
||||
of oBool:
|
||||
result = newType(tyTuple, typ.owner)
|
||||
result.rawAddSon newType(tyBool, typ.owner)
|
||||
result.rawAddSon typ.sons[0]
|
||||
of oEnum:
|
||||
if lastOrd(typ) + 1 < `shl`(BiggestInt(1), 32):
|
||||
result = newType(tyInt32, typ.owner)
|
||||
else:
|
||||
result = newType(tyInt64, typ.owner)
|
||||
|
||||
proc optEnumValue*(typ: PType): BiggestInt =
|
||||
assert typ.kind == tyOpt
|
||||
assert optKind(typ) == oEnum
|
||||
let elem = typ.sons[0].skipTypes(abstractInst).kind
|
||||
if elem == tyBool:
|
||||
result = 2
|
||||
else:
|
||||
assert elem == tyEnum
|
||||
assert typ.n.sons[0].sym.position != low(int)
|
||||
result = typ.n.sons[0].sym.position - 1
|
||||
|
||||
|
||||
const
|
||||
szNonConcreteType* = -3
|
||||
szIllegalRecursion* = -2
|
||||
@@ -1353,13 +1396,21 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
|
||||
of tyStatic:
|
||||
result = if typ.n != nil: computeSizeAux(typ.lastSon, a)
|
||||
else: szUnknownSize
|
||||
of tyOpt:
|
||||
case optKind(typ)
|
||||
of oBool: result = computeSizeAux(lastSon(typ), a) + 1
|
||||
of oEnum:
|
||||
if lastOrd(typ) + 1 < `shl`(BiggestInt(1), 32): result = 4
|
||||
else: result = 8
|
||||
of oNil: result = computeSizeAux(lastSon(typ), a)
|
||||
of oPtr: result = ptrSize
|
||||
else:
|
||||
#internalError("computeSizeAux()")
|
||||
result = szUnknownSize
|
||||
typ.size = result
|
||||
typ.align = int16(a)
|
||||
|
||||
proc computeSize(typ: PType): BiggestInt =
|
||||
proc computeSize*(typ: PType): BiggestInt =
|
||||
var a: BiggestInt = 1
|
||||
result = computeSizeAux(typ, a)
|
||||
|
||||
@@ -1368,7 +1419,7 @@ proc getReturnType*(s: PSym): PType =
|
||||
assert s.kind in skProcKinds
|
||||
result = s.typ.sons[0]
|
||||
|
||||
proc getSize(typ: PType): BiggestInt =
|
||||
proc getSize*(typ: PType): BiggestInt =
|
||||
result = computeSize(typ)
|
||||
if result < 0: internalError("getSize: " & $typ.kind)
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import ast except getstr
|
||||
import
|
||||
strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes,
|
||||
parser, vmdeps, idents, trees, renderer, options, transf, parseutils,
|
||||
vmmarshal
|
||||
vmmarshal, gorgeimpl
|
||||
|
||||
from semfold import leValueConv, ordinalValToString
|
||||
from evaltempl import evalTemplate
|
||||
@@ -66,7 +66,10 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) =
|
||||
stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1)
|
||||
var info = c.debug[pc]
|
||||
# we now use the same format as in system/except.nim
|
||||
var s = toFilename(info)
|
||||
var s = substr(toFilename(info), 0)
|
||||
# this 'substr' prevents a strange corruption. XXX This needs to be
|
||||
# investigated eventually but first attempts to fix it broke everything
|
||||
# see the araq-wip-fixed-writebarrier branch.
|
||||
var line = toLinenumber(info)
|
||||
if line > 0:
|
||||
add(s, '(')
|
||||
@@ -979,7 +982,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
let node = regs[rb+i].regToNode
|
||||
node.info = c.debug[pc]
|
||||
macroCall.add(node)
|
||||
let a = evalTemplate(macroCall, prc, genSymOwner)
|
||||
var a = evalTemplate(macroCall, prc, genSymOwner)
|
||||
if a.kind == nkStmtList and a.len == 1: a = a[0]
|
||||
a.recSetFlagIsRef
|
||||
ensureKind(rkNode)
|
||||
regs[ra].node = a
|
||||
|
||||
@@ -7,50 +7,7 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import ast, types, msgs, os, osproc, streams, options, idents, securehash
|
||||
|
||||
proc readOutput(p: Process): (string, int) =
|
||||
result[0] = ""
|
||||
var output = p.outputStream
|
||||
while not output.atEnd:
|
||||
result[0].add(output.readLine)
|
||||
result[0].add("\n")
|
||||
if result[0].len > 0:
|
||||
result[0].setLen(result[0].len - "\n".len)
|
||||
result[1] = p.waitForExit
|
||||
|
||||
proc opGorge*(cmd, input, cache: string, info: TLineInfo): (string, int) =
|
||||
let workingDir = parentDir(info.toFullPath)
|
||||
if cache.len > 0:# and optForceFullMake notin gGlobalOptions:
|
||||
let h = secureHash(cmd & "\t" & input & "\t" & cache)
|
||||
let filename = options.toGeneratedFile("gorge_" & $h, "txt")
|
||||
var f: File
|
||||
if open(f, filename):
|
||||
result = (f.readAll, 0)
|
||||
f.close
|
||||
return
|
||||
var readSuccessful = false
|
||||
try:
|
||||
var p = startProcess(cmd, workingDir,
|
||||
options={poEvalCommand, poStderrToStdout})
|
||||
if input.len != 0:
|
||||
p.inputStream.write(input)
|
||||
p.inputStream.close()
|
||||
result = p.readOutput
|
||||
readSuccessful = true
|
||||
writeFile(filename, result[0])
|
||||
except IOError, OSError:
|
||||
if not readSuccessful: result = ("", -1)
|
||||
else:
|
||||
try:
|
||||
var p = startProcess(cmd, workingDir,
|
||||
options={poEvalCommand, poStderrToStdout})
|
||||
if input.len != 0:
|
||||
p.inputStream.write(input)
|
||||
p.inputStream.close()
|
||||
result = p.readOutput
|
||||
except IOError, OSError:
|
||||
result = ("", -1)
|
||||
import ast, types, msgs, os, streams, options, idents
|
||||
|
||||
proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
|
||||
try:
|
||||
@@ -250,6 +207,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
|
||||
result = mapTypeToBracket("ref", mRef, t, info)
|
||||
of tyVar: result = mapTypeToBracket("var", mVar, t, info)
|
||||
of tySequence: result = mapTypeToBracket("seq", mSeq, t, info)
|
||||
of tyOpt: result = mapTypeToBracket("opt", mOpt, t, info)
|
||||
of tyProc:
|
||||
if inst:
|
||||
result = newNodeX(nkProcTy)
|
||||
@@ -304,7 +262,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
|
||||
of tyNot: result = mapTypeToBracket("not", mNot, t, info)
|
||||
of tyAnything: result = atomicType("anything", mNone)
|
||||
of tyInferred: internalAssert false
|
||||
of tyStatic, tyFromExpr, tyFieldAccessor:
|
||||
of tyStatic, tyFromExpr:
|
||||
if inst:
|
||||
if t.n != nil: result = t.n.copyTree
|
||||
else: result = atomicType("void", mVoid)
|
||||
@@ -313,7 +271,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
|
||||
result.add atomicType("static", mNone)
|
||||
if t.n != nil:
|
||||
result.add t.n.copyTree
|
||||
of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("mapTypeToAstX")
|
||||
of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("mapTypeToAstX")
|
||||
|
||||
proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode =
|
||||
result = mapTypeToAstX(t, info, false, true)
|
||||
|
||||
@@ -1289,7 +1289,7 @@ proc checkCanEval(c: PCtx; n: PNode) =
|
||||
if s.kind in {skVar, skTemp, skLet, skParam, skResult} and
|
||||
not s.isOwnedBy(c.prc.sym) and s.owner != c.module and c.mode != emRepl:
|
||||
cannotEval(n)
|
||||
elif s.kind in {skProc, skConverter, skMethod,
|
||||
elif s.kind in {skProc, skFunc, skConverter, skMethod,
|
||||
skIterator} and sfForward in s.flags:
|
||||
cannotEval(n)
|
||||
|
||||
@@ -1537,6 +1537,8 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
|
||||
addSon(result, getNullValue(t.sons[i], info))
|
||||
of tySet:
|
||||
result = newNodeIT(nkCurly, info, t)
|
||||
of tyOpt:
|
||||
result = newNodeIT(nkNilLit, info, t)
|
||||
else:
|
||||
globalError(info, "cannot create null element for: " & $t.kind)
|
||||
|
||||
@@ -1712,7 +1714,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
case s.kind
|
||||
of skVar, skForVar, skTemp, skLet, skParam, skResult:
|
||||
genRdVar(c, n, dest, flags)
|
||||
of skProc, skConverter, skMacro, skTemplate, skMethod, skIterator:
|
||||
of skProc, skFunc, skConverter, skMacro, skTemplate, skMethod, skIterator:
|
||||
# 'skTemplate' is only allowed for 'getAst' support:
|
||||
if procIsCallback(c, s): discard
|
||||
elif sfImportc in s.flags: c.importcSym(n.info, s)
|
||||
|
||||
@@ -30,7 +30,7 @@ type
|
||||
wMacro, wMethod, wMixin, wMod, wNil,
|
||||
wNot, wNotin, wObject, wOf, wOr, wOut, wProc, wPtr, wRaise, wRef, wReturn,
|
||||
wShl, wShr, wStatic, wTemplate, wTry, wTuple, wType, wUsing, wVar,
|
||||
wWhen, wWhile, wWith, wWithout, wXor, wYield,
|
||||
wWhen, wWhile, wXor, wYield,
|
||||
|
||||
wColon, wColonColon, wEquals, wDot, wDotDot,
|
||||
wStar, wMinus,
|
||||
@@ -116,7 +116,7 @@ const
|
||||
"out", "proc", "ptr", "raise", "ref", "return",
|
||||
"shl", "shr", "static",
|
||||
"template", "try", "tuple", "type", "using", "var",
|
||||
"when", "while", "with", "without", "xor",
|
||||
"when", "while", "xor",
|
||||
"yield",
|
||||
|
||||
":", "::", "=", ".", "..",
|
||||
|
||||
@@ -248,6 +248,8 @@ proc markWriteOrEscape(w: var W) =
|
||||
for p in a.dest:
|
||||
if p.kind == skParam and p.owner == w.owner:
|
||||
incl(p.flags, sfWrittenTo)
|
||||
if w.owner.kind == skFunc and p.typ.kind != tyVar:
|
||||
localError(a.info, "write access to non-var parameter: " & p.name.s)
|
||||
|
||||
if {rootIsResultOrParam, rootIsHeapAccess, markAsEscaping}*a.destInfo != {}:
|
||||
var destIsParam = false
|
||||
|
||||
@@ -16,6 +16,6 @@ shl shr static
|
||||
template try tuple type
|
||||
using
|
||||
var
|
||||
when while with without
|
||||
when while
|
||||
xor
|
||||
yield
|
||||
|
||||
@@ -633,6 +633,9 @@ the ``vtptr`` magic produced types bound to ``ptr`` types.
|
||||
Symbol lookup in generics
|
||||
-------------------------
|
||||
|
||||
Open and Closed symbols
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The symbol binding rules in generics are slightly subtle: There are "open" and
|
||||
"closed" symbols. A "closed" symbol cannot be re-bound in the instantiation
|
||||
context, an "open" symbol can. Per default overloaded symbols are open
|
||||
@@ -658,6 +661,9 @@ the ``Index`` type is defined *after* the ``==`` for tuples; yet the example
|
||||
compiles as the instantiation takes the currently defined symbols into account
|
||||
too.
|
||||
|
||||
Mixin statement
|
||||
---------------
|
||||
|
||||
A symbol can be forced to be open by a `mixin`:idx: declaration:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
@@ -547,9 +547,6 @@ Instead of:
|
||||
Using statement
|
||||
---------------
|
||||
|
||||
**Warning**: The ``using`` statement is experimental and has to be
|
||||
explicitly enabled with the `experimental`:idx: pragma or command line option!
|
||||
|
||||
The using statement provides syntactic convenience in modules where
|
||||
the same parameter names and types are used over and over. Instead of:
|
||||
|
||||
@@ -563,7 +560,6 @@ name ``c`` should default to type ``Context``, ``n`` should default to
|
||||
``Node`` etc.:
|
||||
|
||||
.. code-block:: nim
|
||||
{.experimental.}
|
||||
using
|
||||
c: Context
|
||||
n: Node
|
||||
|
||||
@@ -550,8 +550,7 @@ order. The *names* of the fields also have to be identical.
|
||||
|
||||
The assignment operator for tuples copies each component.
|
||||
The default assignment operator for objects copies each component. Overloading
|
||||
of the assignment operator for objects is not possible, but this will change
|
||||
in future versions of the compiler.
|
||||
of the assignment operator is described in `type-bound-operations-operator`_.
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
|
||||
@@ -185,9 +185,8 @@ to a storage location:
|
||||
var x = "abc" # introduces a new variable `x` and assigns a value to it
|
||||
x = "xyz" # assigns a new value to `x`
|
||||
|
||||
``=`` is the *assignment operator*. The assignment operator cannot be
|
||||
overloaded, overwritten or forbidden, but this might change in a future version
|
||||
of Nim. You can declare multiple variables with a single assignment
|
||||
``=`` is the *assignment operator*. The assignment operator can be
|
||||
overloaded. You can declare multiple variables with a single assignment
|
||||
statement and all the variables will have the same value:
|
||||
|
||||
.. code-block::
|
||||
|
||||
3
koch.nim
3
koch.nim
@@ -262,6 +262,9 @@ proc buildTools(latest: bool) =
|
||||
let nimgrepExe = "bin/nimgrep".exe
|
||||
nimexec "c -o:" & nimgrepExe & " tools/nimgrep.nim"
|
||||
when defined(windows): buildVccTool()
|
||||
|
||||
nimexec "c -o:" & ("bin/nimresolve".exe) & " tools/nimresolve.nim"
|
||||
|
||||
buildNimble(latest)
|
||||
|
||||
proc nsis(args: string) =
|
||||
|
||||
@@ -75,7 +75,8 @@ type
|
||||
nnkClosure,
|
||||
nnkGotoState,
|
||||
nnkState,
|
||||
nnkBreakState
|
||||
nnkBreakState,
|
||||
nnkFuncDef
|
||||
|
||||
NimNodeKinds* = set[NimNodeKind]
|
||||
NimTypeKind* = enum # some types are no longer used, see ast.nim
|
||||
@@ -96,14 +97,14 @@ type
|
||||
ntyError,
|
||||
ntyBuiltinTypeClass, ntyUserTypeClass, ntyUserTypeClassInst,
|
||||
ntyCompositeTypeClass, ntyInferred, ntyAnd, ntyOr, ntyNot,
|
||||
ntyAnything, ntyStatic, ntyFromExpr, ntyFieldAccessor, ntyVoid
|
||||
ntyAnything, ntyStatic, ntyFromExpr, ntyOpt, ntyVoid
|
||||
|
||||
TNimTypeKinds* {.deprecated.} = set[NimTypeKind]
|
||||
NimSymKind* = enum
|
||||
nskUnknown, nskConditional, nskDynLib, nskParam,
|
||||
nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet,
|
||||
nskConst, nskResult,
|
||||
nskProc, nskMethod, nskIterator,
|
||||
nskProc, nskFunc, nskMethod, nskIterator,
|
||||
nskConverter, nskMacro, nskTemplate, nskField,
|
||||
nskEnumField, nskForVar, nskLabel,
|
||||
nskStub
|
||||
@@ -843,7 +844,8 @@ proc last*(node: NimNode): NimNode {.compileTime.} = node[<node.len]
|
||||
|
||||
|
||||
const
|
||||
RoutineNodes* = {nnkProcDef, nnkMethodDef, nnkDo, nnkLambda, nnkIteratorDef, nnkTemplateDef, nnkConverterDef}
|
||||
RoutineNodes* = {nnkProcDef, nnkFuncDef, nnkMethodDef, nnkDo, nnkLambda,
|
||||
nnkIteratorDef, nnkTemplateDef, nnkConverterDef}
|
||||
AtomicNodes* = {nnkNone..nnkNilLit}
|
||||
CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
|
||||
nnkCallStrLit, nnkHiddenCallConv}
|
||||
|
||||
@@ -54,6 +54,7 @@ type
|
||||
akUInt16 = 42, ## any represents an unsigned in16
|
||||
akUInt32 = 43, ## any represents an unsigned int32
|
||||
akUInt64 = 44, ## any represents an unsigned int64
|
||||
# akOpt = 44+18 ## the builtin 'opt' type.
|
||||
|
||||
Any* = object ## can represent any nim value; NOTE: the wrapped
|
||||
## value can be modified with its wrapper! This means
|
||||
|
||||
@@ -31,8 +31,12 @@ struct Nim::SysThread
|
||||
void entry() override {
|
||||
(_func)(_arg); }
|
||||
|
||||
Thread(Genode::Env &env, Genode::size_t stack_size, Entry func, void *arg)
|
||||
: Genode::Thread(env, "nim-thread", stack_size), _func(func), _arg(arg)
|
||||
Thread(Genode::Env &env, Genode::size_t stack_size, Entry func, void *arg, int affinity)
|
||||
: Genode::Thread(env, "nim-thread", stack_size,
|
||||
env.cpu().affinity_space().location_of_index(affinity),
|
||||
Genode::Cpu_session::Weight(Genode::Cpu_session::Weight::DEFAULT_WEIGHT-1),
|
||||
env.cpu()),
|
||||
_func(func), _arg(arg)
|
||||
{
|
||||
Genode::Thread::start();
|
||||
}
|
||||
@@ -40,8 +44,8 @@ struct Nim::SysThread
|
||||
|
||||
Genode::Constructible<Thread> _thread;
|
||||
|
||||
void initThread(Genode::Env *env, Genode::size_t stack_size, Entry func, void *arg) {
|
||||
_thread.construct(*env, stack_size, func, arg); }
|
||||
void initThread(Genode::Env *env, Genode::size_t stack_size, Entry func, void *arg, int aff) {
|
||||
_thread.construct(*env, stack_size, func, arg, aff); }
|
||||
|
||||
void joinThread() {
|
||||
_thread->join(); }
|
||||
|
||||
@@ -129,7 +129,8 @@ proc tryExec*(db: DbConn, query: SqlQuery,
|
||||
var q = dbFormat(query, args)
|
||||
var stmt: sqlite3.Pstmt
|
||||
if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK:
|
||||
if step(stmt) == SQLITE_DONE:
|
||||
let x = step(stmt)
|
||||
if x in {SQLITE_DONE, SQLITE_ROW}:
|
||||
result = finalize(stmt) == SQLITE_OK
|
||||
|
||||
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||
|
||||
@@ -300,7 +300,7 @@ macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V],
|
||||
result = quote do:
|
||||
(`dotOp`(`obj`, `field`))()
|
||||
for elem in args:
|
||||
result[0].add elem
|
||||
result.add elem
|
||||
|
||||
# Iterators:
|
||||
|
||||
@@ -471,7 +471,7 @@ macro bindMethod*(procedure: typed): auto =
|
||||
# construct the `this` parameter:
|
||||
thisQuote = quote do:
|
||||
var `this` {. nodecl, importc .} : `thisType`
|
||||
call = newNimNode(nnkCall).add(rawProc[0], thisQuote[0][0][0][0])
|
||||
call = newNimNode(nnkCall).add(rawProc[0], thisQuote[0][0][0])
|
||||
# construct the procedure call inside the method
|
||||
if args.len > 2:
|
||||
for idx in 2..args.len-1:
|
||||
@@ -483,6 +483,6 @@ macro bindMethod*(procedure: typed): auto =
|
||||
params,
|
||||
rawProc[4],
|
||||
rawProc[5],
|
||||
newTree(nnkStmtList, thisQuote[0], call)
|
||||
newTree(nnkStmtList, thisQuote, call)
|
||||
)
|
||||
result = body
|
||||
|
||||
@@ -373,11 +373,13 @@ static N_INLINE(NI32, float32ToInt32)(float x) {
|
||||
|
||||
#define float64ToInt64(x) ((NI64) (x))
|
||||
|
||||
#define NIM_STRLIT_FLAG ((NU)(1) << ((NIM_INTBITS) - 2)) /* This has to be the same as system.strlitFlag! */
|
||||
|
||||
#define STRING_LITERAL(name, str, length) \
|
||||
static const struct { \
|
||||
TGenericSeq Sup; \
|
||||
NIM_CHAR data[(length) + 1]; \
|
||||
} name = {{length, length}, str}
|
||||
static const struct { \
|
||||
TGenericSeq Sup; \
|
||||
NIM_CHAR data[(length) + 1]; \
|
||||
} name = {{length, (NI) ((NU)length | NIM_STRLIT_FLAG)}, str}
|
||||
|
||||
typedef struct TStringDesc* string;
|
||||
|
||||
|
||||
@@ -58,8 +58,8 @@ const
|
||||
"interface", "is", "isnot", "iterator", "let", "macro", "method",
|
||||
"mixin", "mod", "nil", "not", "notin", "object", "of", "or", "out", "proc",
|
||||
"ptr", "raise", "ref", "return", "shl", "shr", "static",
|
||||
"template", "try", "tuple", "type", "using", "var", "when", "while", "with",
|
||||
"without", "xor", "yield"]
|
||||
"template", "try", "tuple", "type", "using", "var", "when", "while",
|
||||
"xor", "yield"]
|
||||
|
||||
proc getSourceLanguage*(name: string): SourceLanguage =
|
||||
for i in countup(succ(low(SourceLanguage)), high(SourceLanguage)):
|
||||
|
||||
@@ -1,855 +0,0 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2013 Robert Persson
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import math
|
||||
import strutils
|
||||
|
||||
|
||||
## Basic 2d support with vectors, points, matrices and some basic utilities.
|
||||
## Vectors are implemented as direction vectors, ie. when transformed with a matrix
|
||||
## the translation part of matrix is ignored.
|
||||
## Operators `+` , `-` , `*` , `/` , `+=` , `-=` , `*=` and `/=` are implemented for vectors and scalars.
|
||||
##
|
||||
## Quick start example:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## # Create a matrix which first rotates, then scales and at last translates
|
||||
##
|
||||
## var m:Matrix2d=rotate(DEG90) & scale(2.0) & move(100.0,200.0)
|
||||
##
|
||||
## # Create a 2d point at (100,0) and a vector (5,2)
|
||||
##
|
||||
## var pt:Point2d=point2d(100.0,0.0)
|
||||
##
|
||||
## var vec:Vector2d=vector2d(5.0,2.0)
|
||||
##
|
||||
##
|
||||
## pt &= m # transforms pt in place
|
||||
##
|
||||
## var pt2:Point2d=pt & m #concatenates pt with m and returns a new point
|
||||
##
|
||||
## var vec2:Vector2d=vec & m #concatenates vec with m and returns a new vector
|
||||
|
||||
|
||||
const
|
||||
DEG360* = PI * 2.0
|
||||
## 360 degrees in radians.
|
||||
DEG270* = PI * 1.5
|
||||
## 270 degrees in radians.
|
||||
DEG180* = PI
|
||||
## 180 degrees in radians.
|
||||
DEG90* = PI / 2.0
|
||||
## 90 degrees in radians.
|
||||
DEG60* = PI / 3.0
|
||||
## 60 degrees in radians.
|
||||
DEG45* = PI / 4.0
|
||||
## 45 degrees in radians.
|
||||
DEG30* = PI / 6.0
|
||||
## 30 degrees in radians.
|
||||
DEG15* = PI / 12.0
|
||||
## 15 degrees in radians.
|
||||
RAD2DEGCONST = 180.0 / PI
|
||||
## used internally by DegToRad and RadToDeg
|
||||
|
||||
type
|
||||
Matrix2d* = object
|
||||
## Implements a row major 2d matrix, which means
|
||||
## transformations are applied the order they are concatenated.
|
||||
## The rightmost column of the 3x3 matrix is left out since normally
|
||||
## not used for geometric transformations in 2d.
|
||||
ax*,ay*,bx*,by*,tx*,ty*:float
|
||||
Point2d* = object
|
||||
## Implements a non-homogeneous 2d point stored as
|
||||
## an `x` coordinate and an `y` coordinate.
|
||||
x*,y*:float
|
||||
Vector2d* = object
|
||||
## Implements a 2d **direction vector** stored as
|
||||
## an `x` coordinate and an `y` coordinate. Direction vector means,
|
||||
## that when transforming a vector with a matrix, the translational
|
||||
## part of the matrix is ignored.
|
||||
x*,y*:float
|
||||
{.deprecated: [TMatrix2d: Matrix2d, TPoint2d: Point2d, TVector2d: Vector2d].}
|
||||
|
||||
|
||||
# Some forward declarations...
|
||||
proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d {.noInit.}
|
||||
## Creates a new matrix.
|
||||
## `ax`,`ay` is the local x axis
|
||||
## `bx`,`by` is the local y axis
|
||||
## `tx`,`ty` is the translation
|
||||
proc vector2d*(x,y:float):Vector2d {.noInit,inline.}
|
||||
## Returns a new vector (`x`,`y`)
|
||||
proc point2d*(x,y:float):Point2d {.noInit,inline.}
|
||||
## Returns a new point (`x`,`y`)
|
||||
|
||||
|
||||
|
||||
let
|
||||
IDMATRIX*:Matrix2d=matrix2d(1.0,0.0,0.0,1.0,0.0,0.0)
|
||||
## Quick access to an identity matrix
|
||||
ORIGO*:Point2d=point2d(0.0,0.0)
|
||||
## Quick access to point (0,0)
|
||||
XAXIS*:Vector2d=vector2d(1.0,0.0)
|
||||
## Quick access to an 2d x-axis unit vector
|
||||
YAXIS*:Vector2d=vector2d(0.0,1.0)
|
||||
## Quick access to an 2d y-axis unit vector
|
||||
|
||||
|
||||
# ***************************************
|
||||
# Private utils
|
||||
# ***************************************
|
||||
|
||||
proc rtos(val:float):string=
|
||||
return formatFloat(val,ffDefault,0)
|
||||
|
||||
proc safeArccos(v:float):float=
|
||||
## assumes v is in range 0.0-1.0, but clamps
|
||||
## the value to avoid out of domain errors
|
||||
## due to rounding issues
|
||||
return arccos(clamp(v,-1.0,1.0))
|
||||
|
||||
|
||||
template makeBinOpVector(s) =
|
||||
## implements binary operators ``+``, ``-``, ``*`` and ``/`` for vectors
|
||||
proc s*(a,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a.x,b.x),s(a.y,b.y))
|
||||
proc s*(a:Vector2d,b:float):Vector2d {.inline,noInit.} = vector2d(s(a.x,b),s(a.y,b))
|
||||
proc s*(a:float,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a,b.x),s(a,b.y))
|
||||
|
||||
template makeBinOpAssignVector(s)=
|
||||
## implements inplace binary operators ``+=``, ``-=``, ``/=`` and ``*=`` for vectors
|
||||
proc s*(a:var Vector2d,b:Vector2d) {.inline.} = s(a.x,b.x) ; s(a.y,b.y)
|
||||
proc s*(a:var Vector2d,b:float) {.inline.} = s(a.x,b) ; s(a.y,b)
|
||||
|
||||
|
||||
# ***************************************
|
||||
# Matrix2d implementation
|
||||
# ***************************************
|
||||
|
||||
proc setElements*(t:var Matrix2d,ax,ay,bx,by,tx,ty:float) {.inline.}=
|
||||
## Sets arbitrary elements in an existing matrix.
|
||||
t.ax=ax
|
||||
t.ay=ay
|
||||
t.bx=bx
|
||||
t.by=by
|
||||
t.tx=tx
|
||||
t.ty=ty
|
||||
|
||||
proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d =
|
||||
result.setElements(ax,ay,bx,by,tx,ty)
|
||||
|
||||
proc `&`*(a,b:Matrix2d):Matrix2d {.noInit.} = #concatenate matrices
|
||||
## Concatenates matrices returning a new matrix.
|
||||
|
||||
# | a.AX a.AY 0 | | b.AX b.AY 0 |
|
||||
# | a.BX a.BY 0 | * | b.BX b.BY 0 |
|
||||
# | a.TX a.TY 1 | | b.TX b.TY 1 |
|
||||
result.setElements(
|
||||
a.ax * b.ax + a.ay * b.bx,
|
||||
a.ax * b.ay + a.ay * b.by,
|
||||
a.bx * b.ax + a.by * b.bx,
|
||||
a.bx * b.ay + a.by * b.by,
|
||||
a.tx * b.ax + a.ty * b.bx + b.tx,
|
||||
a.tx * b.ay + a.ty * b.by + b.ty)
|
||||
|
||||
|
||||
proc scale*(s:float):Matrix2d {.noInit.} =
|
||||
## Returns a new scale matrix.
|
||||
result.setElements(s,0,0,s,0,0)
|
||||
|
||||
proc scale*(s:float,org:Point2d):Matrix2d {.noInit.} =
|
||||
## Returns a new scale matrix using, `org` as scale origin.
|
||||
result.setElements(s,0,0,s,org.x-s*org.x,org.y-s*org.y)
|
||||
|
||||
proc stretch*(sx,sy:float):Matrix2d {.noInit.} =
|
||||
## Returns new a stretch matrix, which is a
|
||||
## scale matrix with non uniform scale in x and y.
|
||||
result.setElements(sx,0,0,sy,0,0)
|
||||
|
||||
proc stretch*(sx,sy:float,org:Point2d):Matrix2d {.noInit.} =
|
||||
## Returns a new stretch matrix, which is a
|
||||
## scale matrix with non uniform scale in x and y.
|
||||
## `org` is used as stretch origin.
|
||||
result.setElements(sx,0,0,sy,org.x-sx*org.x,org.y-sy*org.y)
|
||||
|
||||
proc move*(dx,dy:float):Matrix2d {.noInit.} =
|
||||
## Returns a new translation matrix.
|
||||
result.setElements(1,0,0,1,dx,dy)
|
||||
|
||||
proc move*(v:Vector2d):Matrix2d {.noInit.} =
|
||||
## Returns a new translation matrix from a vector.
|
||||
result.setElements(1,0,0,1,v.x,v.y)
|
||||
|
||||
proc rotate*(rad:float):Matrix2d {.noInit.} =
|
||||
## Returns a new rotation matrix, which
|
||||
## represents a rotation by `rad` radians
|
||||
let
|
||||
s=sin(rad)
|
||||
c=cos(rad)
|
||||
result.setElements(c,s,-s,c,0,0)
|
||||
|
||||
proc rotate*(rad:float,org:Point2d):Matrix2d {.noInit.} =
|
||||
## Returns a new rotation matrix, which
|
||||
## represents a rotation by `rad` radians around
|
||||
## the origin `org`
|
||||
let
|
||||
s=sin(rad)
|
||||
c=cos(rad)
|
||||
result.setElements(c,s,-s,c,org.x+s*org.y-c*org.x,org.y-c*org.y-s*org.x)
|
||||
|
||||
proc mirror*(v:Vector2d):Matrix2d {.noInit.} =
|
||||
## Returns a new mirror matrix, mirroring
|
||||
## around the line that passes through origo and
|
||||
## has the direction of `v`
|
||||
let
|
||||
sqx=v.x*v.x
|
||||
sqy=v.y*v.y
|
||||
nd=1.0/(sqx+sqy) #used to normalize invector
|
||||
xy2=v.x*v.y*2.0*nd
|
||||
sqd=nd*(sqx-sqy)
|
||||
|
||||
if nd==Inf or nd==NegInf:
|
||||
return IDMATRIX #mirroring around a zero vector is arbitrary=>just use identity
|
||||
|
||||
result.setElements(
|
||||
sqd,xy2,
|
||||
xy2,-sqd,
|
||||
0.0,0.0)
|
||||
|
||||
proc mirror*(org:Point2d,v:Vector2d):Matrix2d {.noInit.} =
|
||||
## Returns a new mirror matrix, mirroring
|
||||
## around the line that passes through `org` and
|
||||
## has the direction of `v`
|
||||
let
|
||||
sqx=v.x*v.x
|
||||
sqy=v.y*v.y
|
||||
nd=1.0/(sqx+sqy) #used to normalize invector
|
||||
xy2=v.x*v.y*2.0*nd
|
||||
sqd=nd*(sqx-sqy)
|
||||
|
||||
if nd==Inf or nd==NegInf:
|
||||
return IDMATRIX #mirroring around a zero vector is arbitrary=>just use identity
|
||||
|
||||
result.setElements(
|
||||
sqd,xy2,
|
||||
xy2,-sqd,
|
||||
org.x-org.y*xy2-org.x*sqd,org.y-org.x*xy2+org.y*sqd)
|
||||
|
||||
|
||||
|
||||
proc skew*(xskew,yskew:float):Matrix2d {.noInit.} =
|
||||
## Returns a new skew matrix, which has its
|
||||
## x axis rotated `xskew` radians from the local x axis, and
|
||||
## y axis rotated `yskew` radians from the local y axis
|
||||
result.setElements(cos(yskew),sin(yskew),-sin(xskew),cos(xskew),0,0)
|
||||
|
||||
|
||||
proc `$`* (t:Matrix2d):string {.noInit.} =
|
||||
## Returns a string representation of the matrix
|
||||
return rtos(t.ax) & "," & rtos(t.ay) &
|
||||
"," & rtos(t.bx) & "," & rtos(t.by) &
|
||||
"," & rtos(t.tx) & "," & rtos(t.ty)
|
||||
|
||||
proc isUniform*(t:Matrix2d,tol=1.0e-6):bool=
|
||||
## Checks if the transform is uniform, that is
|
||||
## perpendicular axes of equal length, which means (for example)
|
||||
## it cannot transform a circle into an ellipse.
|
||||
## `tol` is used as tolerance for both equal length comparison
|
||||
## and perp. comparison.
|
||||
|
||||
#dot product=0 means perpendicular coord. system:
|
||||
if abs(t.ax*t.bx+t.ay*t.by)<=tol:
|
||||
#subtract squared lengths of axes to check if uniform scaling:
|
||||
if abs((t.ax*t.ax+t.ay*t.ay)-(t.bx*t.bx+t.by*t.by))<=tol:
|
||||
return true
|
||||
return false
|
||||
|
||||
proc determinant*(t:Matrix2d):float=
|
||||
## Computes the determinant of the matrix.
|
||||
|
||||
#NOTE: equivalent with perp.dot product for two 2d vectors
|
||||
return t.ax*t.by-t.bx*t.ay
|
||||
|
||||
proc isMirroring* (m:Matrix2d):bool=
|
||||
## Checks if the `m` is a mirroring matrix,
|
||||
## which means it will reverse direction of a curve transformed with it
|
||||
return m.determinant<0.0
|
||||
|
||||
proc inverse*(m:Matrix2d):Matrix2d {.noInit.} =
|
||||
## Returns a new matrix, which is the inverse of the matrix
|
||||
## If the matrix is not invertible (determinant=0), an EDivByZero
|
||||
## will be raised.
|
||||
let d=m.determinant
|
||||
if d==0.0:
|
||||
raise newException(DivByZeroError,"Cannot invert a zero determinant matrix")
|
||||
|
||||
result.setElements(
|
||||
m.by/d,-m.ay/d,
|
||||
-m.bx/d,m.ax/d,
|
||||
(m.bx*m.ty-m.by*m.tx)/d,
|
||||
(m.ay*m.tx-m.ax*m.ty)/d)
|
||||
|
||||
proc equals*(m1:Matrix2d,m2:Matrix2d,tol=1.0e-6):bool=
|
||||
## Checks if all elements of `m1`and `m2` is equal within
|
||||
## a given tolerance `tol`.
|
||||
return
|
||||
abs(m1.ax-m2.ax)<=tol and
|
||||
abs(m1.ay-m2.ay)<=tol and
|
||||
abs(m1.bx-m2.bx)<=tol and
|
||||
abs(m1.by-m2.by)<=tol and
|
||||
abs(m1.tx-m2.tx)<=tol and
|
||||
abs(m1.ty-m2.ty)<=tol
|
||||
|
||||
proc `=~`*(m1,m2:Matrix2d):bool=
|
||||
## Checks if `m1`and `m2` is approximately equal, using a
|
||||
## tolerance of 1e-6.
|
||||
equals(m1,m2)
|
||||
|
||||
proc isIdentity*(m:Matrix2d,tol=1.0e-6):bool=
|
||||
## Checks is a matrix is approximately an identity matrix,
|
||||
## using `tol` as tolerance for each element.
|
||||
return equals(m,IDMATRIX,tol)
|
||||
|
||||
proc apply*(m:Matrix2d,x,y:var float,translate=false)=
|
||||
## Applies transformation `m` onto `x`,`y`, optionally
|
||||
## using the translation part of the matrix.
|
||||
if translate: # positional style transform
|
||||
let newx=x*m.ax+y*m.bx+m.tx
|
||||
y=x*m.ay+y*m.by+m.ty
|
||||
x=newx
|
||||
else: # delta style transform
|
||||
let newx=x*m.ax+y*m.bx
|
||||
y=x*m.ay+y*m.by
|
||||
x=newx
|
||||
|
||||
|
||||
|
||||
# ***************************************
|
||||
# Vector2d implementation
|
||||
# ***************************************
|
||||
proc vector2d*(x,y:float):Vector2d = #forward decl.
|
||||
result.x=x
|
||||
result.y=y
|
||||
|
||||
proc polarVector2d*(ang:float,len:float):Vector2d {.noInit.} =
|
||||
## Returns a new vector with angle `ang` and magnitude `len`
|
||||
result.x=cos(ang)*len
|
||||
result.y=sin(ang)*len
|
||||
|
||||
proc slopeVector2d*(slope:float,len:float):Vector2d {.noInit.} =
|
||||
## Returns a new vector having slope (dy/dx) given by
|
||||
## `slope`, and a magnitude of `len`
|
||||
let ang=arctan(slope)
|
||||
result.x=cos(ang)*len
|
||||
result.y=sin(ang)*len
|
||||
|
||||
proc len*(v:Vector2d):float {.inline.}=
|
||||
## Returns the length of the vector.
|
||||
sqrt(v.x*v.x+v.y*v.y)
|
||||
|
||||
proc `len=`*(v:var Vector2d,newlen:float) {.noInit.} =
|
||||
## Sets the length of the vector, keeping its angle.
|
||||
let fac=newlen/v.len
|
||||
|
||||
if newlen==0.0:
|
||||
v.x=0.0
|
||||
v.y=0.0
|
||||
return
|
||||
|
||||
if fac==Inf or fac==NegInf:
|
||||
#to short for float accuracy
|
||||
#do as good as possible:
|
||||
v.x=newlen
|
||||
v.y=0.0
|
||||
else:
|
||||
v.x*=fac
|
||||
v.y*=fac
|
||||
|
||||
proc sqrLen*(v:Vector2d):float {.inline.}=
|
||||
## Computes the squared length of the vector, which is
|
||||
## faster than computing the absolute length.
|
||||
v.x*v.x+v.y*v.y
|
||||
|
||||
proc angle*(v:Vector2d):float=
|
||||
## Returns the angle of the vector.
|
||||
## (The counter clockwise plane angle between posetive x axis and `v`)
|
||||
result=arctan2(v.y,v.x)
|
||||
if result<0.0: result+=DEG360
|
||||
|
||||
proc `$` *(v:Vector2d):string=
|
||||
## String representation of `v`
|
||||
result=rtos(v.x)
|
||||
result.add(",")
|
||||
result.add(rtos(v.y))
|
||||
|
||||
|
||||
proc `&` *(v:Vector2d,m:Matrix2d):Vector2d {.noInit.} =
|
||||
## Concatenate vector `v` with a transformation matrix.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix.
|
||||
|
||||
# | AX AY 0 |
|
||||
# | X Y 1 | * | BX BY 0 |
|
||||
# | 0 0 1 |
|
||||
result.x=v.x*m.ax+v.y*m.bx
|
||||
result.y=v.x*m.ay+v.y*m.by
|
||||
|
||||
|
||||
proc `&=`*(v:var Vector2d,m:Matrix2d) {.inline.}=
|
||||
## Applies transformation `m` onto `v` in place.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix.
|
||||
|
||||
# | AX AY 0 |
|
||||
# | X Y 1 | * | BX BY 0 |
|
||||
# | 0 0 1 |
|
||||
let newx=v.x*m.ax+v.y*m.bx
|
||||
v.y=v.x*m.ay+v.y*m.by
|
||||
v.x=newx
|
||||
|
||||
|
||||
proc tryNormalize*(v:var Vector2d):bool=
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length (and thus no angle), it is left unmodified and
|
||||
## false is returned, otherwise true is returned.
|
||||
|
||||
let mag=v.len
|
||||
|
||||
if mag==0.0:
|
||||
return false
|
||||
|
||||
v.x/=mag
|
||||
v.y/=mag
|
||||
return true
|
||||
|
||||
|
||||
proc normalize*(v:var Vector2d) {.inline.}=
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length, an EDivByZero will be raised.
|
||||
if not tryNormalize(v):
|
||||
raise newException(DivByZeroError,"Cannot normalize zero length vector")
|
||||
|
||||
proc transformNorm*(v:var Vector2d,t:Matrix2d)=
|
||||
## Applies a normal direction transformation `t` onto `v` in place.
|
||||
## The resulting vector is *not* normalized. Transforming a vector ignores the
|
||||
## translational part of the matrix. If the matrix is not invertible
|
||||
## (determinant=0), an EDivByZero will be raised.
|
||||
|
||||
# transforming a normal is done by transforming
|
||||
# by the transpose of the inverse of the original matrix
|
||||
# this can be heavily optimized by precompute and inline
|
||||
# | | AX AY 0 | ^-1| ^T
|
||||
# | X Y 1 | * | | BX BY 0 | |
|
||||
# | | 0 0 1 | |
|
||||
let d=t.determinant
|
||||
if(d==0.0):
|
||||
raise newException(DivByZeroError,"Matrix is not invertible")
|
||||
let newx = (t.by*v.x-t.ay*v.y)/d
|
||||
v.y = (t.ax*v.y-t.bx*v.x)/d
|
||||
v.x = newx
|
||||
|
||||
proc transformInv*(v:var Vector2d,t:Matrix2d)=
|
||||
## Applies inverse of a transformation `t` to `v` in place.
|
||||
## This is faster than creating an inverse matrix and apply() it.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix. If the matrix is not invertible (determinant=0), an EDivByZero
|
||||
## will be raised.
|
||||
let d=t.determinant
|
||||
|
||||
if(d==0.0):
|
||||
raise newException(DivByZeroError,"Matrix is not invertible")
|
||||
|
||||
let newx=(t.by*v.x-t.bx*v.y)/d
|
||||
v.y = (t.ax*v.y-t.ay*v.x)/d
|
||||
v.x = newx
|
||||
|
||||
proc transformNormInv*(v:var Vector2d,t:Matrix2d)=
|
||||
## Applies an inverse normal direction transformation `t` onto `v` in place.
|
||||
## This is faster than creating an inverse
|
||||
## matrix and transformNorm(...) it. Transforming a vector ignores the
|
||||
## translational part of the matrix.
|
||||
|
||||
# normal inverse transform is done by transforming
|
||||
# by the inverse of the transpose of the inverse of the org. matrix
|
||||
# which is equivalent with transforming with the transpose.
|
||||
# | | | AX AY 0 |^-1|^T|^-1 | AX BX 0 |
|
||||
# | X Y 1 | * | | | BX BY 0 | | | = | X Y 1 | * | AY BY 0 |
|
||||
# | | | 0 0 1 | | | | 0 0 1 |
|
||||
# This can be heavily reduced to:
|
||||
let newx=t.ay*v.y+t.ax*v.x
|
||||
v.y=t.by*v.y+t.bx*v.x
|
||||
v.x=newx
|
||||
|
||||
proc rotate90*(v:var Vector2d) {.inline.}=
|
||||
## Quickly rotates vector `v` 90 degrees counter clockwise,
|
||||
## without using any trigonometrics.
|
||||
swap(v.x,v.y)
|
||||
v.x= -v.x
|
||||
|
||||
proc rotate180*(v:var Vector2d){.inline.}=
|
||||
## Quickly rotates vector `v` 180 degrees counter clockwise,
|
||||
## without using any trigonometrics.
|
||||
v.x= -v.x
|
||||
v.y= -v.y
|
||||
|
||||
proc rotate270*(v:var Vector2d) {.inline.}=
|
||||
## Quickly rotates vector `v` 270 degrees counter clockwise,
|
||||
## without using any trigonometrics.
|
||||
swap(v.x,v.y)
|
||||
v.y= -v.y
|
||||
|
||||
proc rotate*(v:var Vector2d,rad:float) =
|
||||
## Rotates vector `v` `rad` radians in place.
|
||||
let
|
||||
s=sin(rad)
|
||||
c=cos(rad)
|
||||
newx=c*v.x-s*v.y
|
||||
v.y=c*v.y+s*v.x
|
||||
v.x=newx
|
||||
|
||||
proc scale*(v:var Vector2d,fac:float){.inline.}=
|
||||
## Scales vector `v` `rad` radians in place.
|
||||
v.x*=fac
|
||||
v.y*=fac
|
||||
|
||||
proc stretch*(v:var Vector2d,facx,facy:float){.inline.}=
|
||||
## Stretches vector `v` `facx` times horizontally,
|
||||
## and `facy` times vertically.
|
||||
v.x*=facx
|
||||
v.y*=facy
|
||||
|
||||
proc mirror*(v:var Vector2d,mirrvec:Vector2d)=
|
||||
## Mirrors vector `v` using `mirrvec` as mirror direction.
|
||||
let
|
||||
sqx=mirrvec.x*mirrvec.x
|
||||
sqy=mirrvec.y*mirrvec.y
|
||||
nd=1.0/(sqx+sqy) #used to normalize invector
|
||||
xy2=mirrvec.x*mirrvec.y*2.0*nd
|
||||
sqd=nd*(sqx-sqy)
|
||||
|
||||
if nd==Inf or nd==NegInf:
|
||||
return #mirroring around a zero vector is arbitrary=>keep as is is fastest
|
||||
|
||||
let newx=xy2*v.y+sqd*v.x
|
||||
v.y=v.x*xy2-sqd*v.y
|
||||
v.x=newx
|
||||
|
||||
|
||||
proc `-` *(v:Vector2d):Vector2d=
|
||||
## Negates a vector
|
||||
result.x= -v.x
|
||||
result.y= -v.y
|
||||
|
||||
# declare templated binary operators
|
||||
makeBinOpVector(`+`)
|
||||
makeBinOpVector(`-`)
|
||||
makeBinOpVector(`*`)
|
||||
makeBinOpVector(`/`)
|
||||
makeBinOpAssignVector(`+=`)
|
||||
makeBinOpAssignVector(`-=`)
|
||||
makeBinOpAssignVector(`*=`)
|
||||
makeBinOpAssignVector(`/=`)
|
||||
|
||||
|
||||
proc dot*(v1,v2:Vector2d):float=
|
||||
## Computes the dot product of two vectors.
|
||||
## Returns 0.0 if the vectors are perpendicular.
|
||||
return v1.x*v2.x+v1.y*v2.y
|
||||
|
||||
proc cross*(v1,v2:Vector2d):float=
|
||||
## Computes the cross product of two vectors, also called
|
||||
## the 'perpendicular dot product' in 2d. Returns 0.0 if the vectors
|
||||
## are parallel.
|
||||
return v1.x*v2.y-v1.y*v2.x
|
||||
|
||||
proc equals*(v1,v2:Vector2d,tol=1.0e-6):bool=
|
||||
## Checks if two vectors approximately equals with a tolerance.
|
||||
return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol
|
||||
|
||||
proc `=~` *(v1,v2:Vector2d):bool=
|
||||
## Checks if two vectors approximately equals with a
|
||||
## hardcoded tolerance 1e-6
|
||||
equals(v1,v2)
|
||||
|
||||
proc angleTo*(v1,v2:Vector2d):float=
|
||||
## Returns the smallest of the two possible angles
|
||||
## between `v1` and `v2` in radians.
|
||||
var
|
||||
nv1=v1
|
||||
nv2=v2
|
||||
if not nv1.tryNormalize or not nv2.tryNormalize:
|
||||
return 0.0 # zero length vector has zero angle to any other vector
|
||||
return safeArccos(dot(nv1,nv2))
|
||||
|
||||
proc angleCCW*(v1,v2:Vector2d):float=
|
||||
## Returns the counter clockwise plane angle from `v1` to `v2`,
|
||||
## in range 0 - 2*PI
|
||||
let a=v1.angleTo(v2)
|
||||
if v1.cross(v2)>=0.0:
|
||||
return a
|
||||
return DEG360-a
|
||||
|
||||
proc angleCW*(v1,v2:Vector2d):float=
|
||||
## Returns the clockwise plane angle from `v1` to `v2`,
|
||||
## in range 0 - 2*PI
|
||||
let a=v1.angleTo(v2)
|
||||
if v1.cross(v2)<=0.0:
|
||||
return a
|
||||
return DEG360-a
|
||||
|
||||
proc turnAngle*(v1,v2:Vector2d):float=
|
||||
## Returns the amount v1 should be rotated (in radians) to equal v2,
|
||||
## in range -PI to PI
|
||||
let a=v1.angleTo(v2)
|
||||
if v1.cross(v2)<=0.0:
|
||||
return -a
|
||||
return a
|
||||
|
||||
proc bisect*(v1,v2:Vector2d):Vector2d {.noInit.}=
|
||||
## Computes the bisector between v1 and v2 as a normalized vector.
|
||||
## If one of the input vectors has zero length, a normalized version
|
||||
## of the other is returned. If both input vectors has zero length,
|
||||
## an arbitrary normalized vector is returned.
|
||||
var
|
||||
vmag1=v1.len
|
||||
vmag2=v2.len
|
||||
|
||||
# zero length vector equals arbitrary vector, just change to magnitude to one to
|
||||
# avoid zero division
|
||||
if vmag1==0.0:
|
||||
if vmag2==0: #both are zero length return any normalized vector
|
||||
return XAXIS
|
||||
vmag1=1.0
|
||||
if vmag2==0.0: vmag2=1.0
|
||||
|
||||
let
|
||||
x1=v1.x/vmag1
|
||||
y1=v1.y/vmag1
|
||||
x2=v2.x/vmag2
|
||||
y2=v2.y/vmag2
|
||||
|
||||
result.x=(x1 + x2) * 0.5
|
||||
result.y=(y1 + y2) * 0.5
|
||||
|
||||
if not result.tryNormalize():
|
||||
# This can happen if vectors are colinear. In this special case
|
||||
# there are actually two bisectors, we select just
|
||||
# one of them (x1,y1 rotated 90 degrees ccw).
|
||||
result.x = -y1
|
||||
result.y = x1
|
||||
|
||||
|
||||
|
||||
# ***************************************
|
||||
# Point2d implementation
|
||||
# ***************************************
|
||||
|
||||
proc point2d*(x,y:float):Point2d =
|
||||
result.x=x
|
||||
result.y=y
|
||||
|
||||
proc sqrDist*(a,b:Point2d):float=
|
||||
## Computes the squared distance between `a` and `b`
|
||||
let dx=b.x-a.x
|
||||
let dy=b.y-a.y
|
||||
result=dx*dx+dy*dy
|
||||
|
||||
proc dist*(a,b:Point2d):float {.inline.}=
|
||||
## Computes the absolute distance between `a` and `b`
|
||||
result=sqrt(sqrDist(a,b))
|
||||
|
||||
proc angle*(a,b:Point2d):float=
|
||||
## Computes the angle of the vector `b`-`a`
|
||||
let dx=b.x-a.x
|
||||
let dy=b.y-a.y
|
||||
result=arctan2(dy,dx)
|
||||
if result<0:
|
||||
result += DEG360
|
||||
|
||||
proc `$` *(p:Point2d):string=
|
||||
## String representation of `p`
|
||||
result=rtos(p.x)
|
||||
result.add(",")
|
||||
result.add(rtos(p.y))
|
||||
|
||||
proc `&`*(p:Point2d,t:Matrix2d):Point2d {.noInit,inline.} =
|
||||
## Concatenates a point `p` with a transform `t`,
|
||||
## resulting in a new, transformed point.
|
||||
|
||||
# | AX AY 0 |
|
||||
# | X Y 1 | * | BX BY 0 |
|
||||
# | TX TY 1 |
|
||||
result.x=p.x*t.ax+p.y*t.bx+t.tx
|
||||
result.y=p.x*t.ay+p.y*t.by+t.ty
|
||||
|
||||
proc `&=` *(p:var Point2d,t:Matrix2d) {.inline.}=
|
||||
## Applies transformation `t` onto `p` in place.
|
||||
let newx=p.x*t.ax+p.y*t.bx+t.tx
|
||||
p.y=p.x*t.ay+p.y*t.by+t.ty
|
||||
p.x=newx
|
||||
|
||||
|
||||
proc transformInv*(p:var Point2d,t:Matrix2d){.inline.}=
|
||||
## Applies the inverse of transformation `t` onto `p` in place.
|
||||
## If the matrix is not invertable (determinant=0) , EDivByZero will
|
||||
## be raised.
|
||||
|
||||
# | AX AY 0 | ^-1
|
||||
# | X Y 1 | * | BX BY 0 |
|
||||
# | TX TY 1 |
|
||||
let d=t.determinant
|
||||
if d==0.0:
|
||||
raise newException(DivByZeroError,"Cannot invert a zero determinant matrix")
|
||||
let
|
||||
newx= (t.bx*t.ty-t.by*t.tx+p.x*t.by-p.y*t.bx)/d
|
||||
p.y = -(t.ax*t.ty-t.ay*t.tx+p.x*t.ay-p.y*t.ax)/d
|
||||
p.x=newx
|
||||
|
||||
|
||||
proc `+`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} =
|
||||
## Adds a vector `v` to a point `p`, resulting
|
||||
## in a new point.
|
||||
result.x=p.x+v.x
|
||||
result.y=p.y+v.y
|
||||
|
||||
proc `+=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} =
|
||||
## Adds a vector `v` to a point `p` in place.
|
||||
p.x+=v.x
|
||||
p.y+=v.y
|
||||
|
||||
proc `-`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} =
|
||||
## Subtracts a vector `v` from a point `p`, resulting
|
||||
## in a new point.
|
||||
result.x=p.x-v.x
|
||||
result.y=p.y-v.y
|
||||
|
||||
proc `-`*(p1,p2:Point2d):Vector2d {.noInit,inline.} =
|
||||
## Subtracts `p2`from `p1` resulting in a difference vector.
|
||||
result.x=p1.x-p2.x
|
||||
result.y=p1.y-p2.y
|
||||
|
||||
proc `-=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} =
|
||||
## Subtracts a vector `v` from a point `p` in place.
|
||||
p.x-=v.x
|
||||
p.y-=v.y
|
||||
|
||||
proc equals(p1,p2:Point2d,tol=1.0e-6):bool {.inline.}=
|
||||
## Checks if two points approximately equals with a tolerance.
|
||||
return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol
|
||||
|
||||
proc `=~`*(p1,p2:Point2d):bool {.inline.}=
|
||||
## Checks if two vectors approximately equals with a
|
||||
## hardcoded tolerance 1e-6
|
||||
equals(p1,p2)
|
||||
|
||||
proc polar*(p:Point2d,ang,dist:float):Point2d {.noInit.} =
|
||||
## Returns a point with a given angle and distance away from `p`
|
||||
result.x=p.x+cos(ang)*dist
|
||||
result.y=p.y+sin(ang)*dist
|
||||
|
||||
proc rotate*(p:var Point2d,rad:float)=
|
||||
## Rotates a point in place `rad` radians around origo.
|
||||
let
|
||||
c=cos(rad)
|
||||
s=sin(rad)
|
||||
newx=p.x*c-p.y*s
|
||||
p.y=p.y*c+p.x*s
|
||||
p.x=newx
|
||||
|
||||
proc rotate*(p:var Point2d,rad:float,org:Point2d)=
|
||||
## Rotates a point in place `rad` radians using `org` as
|
||||
## center of rotation.
|
||||
let
|
||||
c=cos(rad)
|
||||
s=sin(rad)
|
||||
newx=(p.x - org.x) * c - (p.y - org.y) * s + org.x
|
||||
p.y=(p.y - org.y) * c + (p.x - org.x) * s + org.y
|
||||
p.x=newx
|
||||
|
||||
proc scale*(p:var Point2d,fac:float) {.inline.}=
|
||||
## Scales a point in place `fac` times with world origo as origin.
|
||||
p.x*=fac
|
||||
p.y*=fac
|
||||
|
||||
proc scale*(p:var Point2d,fac:float,org:Point2d){.inline.}=
|
||||
## Scales the point in place `fac` times with `org` as origin.
|
||||
p.x=(p.x - org.x) * fac + org.x
|
||||
p.y=(p.y - org.y) * fac + org.y
|
||||
|
||||
proc stretch*(p:var Point2d,facx,facy:float){.inline.}=
|
||||
## Scales a point in place non uniformly `facx` and `facy` times with
|
||||
## world origo as origin.
|
||||
p.x*=facx
|
||||
p.y*=facy
|
||||
|
||||
proc stretch*(p:var Point2d,facx,facy:float,org:Point2d){.inline.}=
|
||||
## Scales the point in place non uniformly `facx` and `facy` times with
|
||||
## `org` as origin.
|
||||
p.x=(p.x - org.x) * facx + org.x
|
||||
p.y=(p.y - org.y) * facy + org.y
|
||||
|
||||
proc move*(p:var Point2d,dx,dy:float){.inline.}=
|
||||
## Translates a point `dx`, `dy` in place.
|
||||
p.x+=dx
|
||||
p.y+=dy
|
||||
|
||||
proc move*(p:var Point2d,v:Vector2d){.inline.}=
|
||||
## Translates a point with vector `v` in place.
|
||||
p.x+=v.x
|
||||
p.y+=v.y
|
||||
|
||||
proc sgnArea*(a,b,c:Point2d):float=
|
||||
## Computes the signed area of the triangle thru points `a`,`b` and `c`
|
||||
## result>0.0 for counter clockwise triangle
|
||||
## result<0.0 for clockwise triangle
|
||||
## This is commonly used to determinate side of a point with respect to a line.
|
||||
return ((b.x - c.x) * (b.y - a.y)-(b.y - c.y) * (b.x - a.x))*0.5
|
||||
|
||||
proc area*(a,b,c:Point2d):float=
|
||||
## Computes the area of the triangle thru points `a`,`b` and `c`
|
||||
return abs(sgnArea(a,b,c))
|
||||
|
||||
proc closestPoint*(p:Point2d,pts:varargs[Point2d]):Point2d=
|
||||
## Returns a point selected from `pts`, that has the closest
|
||||
## euclidean distance to `p`
|
||||
assert(pts.len>0) # must have at least one point
|
||||
|
||||
var
|
||||
bestidx=0
|
||||
bestdist=p.sqrDist(pts[0])
|
||||
curdist:float
|
||||
|
||||
for idx in 1..high(pts):
|
||||
curdist=p.sqrDist(pts[idx])
|
||||
if curdist<bestdist:
|
||||
bestidx=idx
|
||||
bestdist=curdist
|
||||
|
||||
result=pts[bestidx]
|
||||
|
||||
|
||||
# ***************************************
|
||||
# Misc. math utilities that should
|
||||
# probably be in another module.
|
||||
# ***************************************
|
||||
proc normAngle*(ang:float):float=
|
||||
## Returns an angle in radians, that is equal to `ang`,
|
||||
## but in the range 0 to <2*PI
|
||||
if ang>=0.0 and ang<DEG360:
|
||||
return ang
|
||||
|
||||
return ang mod DEG360
|
||||
|
||||
proc degToRad*(deg:float):float {.inline.}=
|
||||
## converts `deg` degrees to radians
|
||||
deg / RAD2DEGCONST
|
||||
|
||||
proc radToDeg*(rad:float):float {.inline.}=
|
||||
## converts `rad` radians to degrees
|
||||
rad * RAD2DEGCONST
|
||||
1040
lib/pure/basic3d.nim
1040
lib/pure/basic3d.nim
File diff suppressed because it is too large
Load Diff
@@ -287,8 +287,6 @@ proc exclImpl[A](s: var HashSet[A], key: A) : bool {. inline .} =
|
||||
|
||||
if i >= 0:
|
||||
result = false
|
||||
s.data[i].hcode = 0
|
||||
s.data[i].key = default(type(s.data[i].key))
|
||||
dec(s.counter)
|
||||
while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
|
||||
var j = i # The correctness of this depends on (h+1) in nextTry,
|
||||
@@ -300,7 +298,7 @@ proc exclImpl[A](s: var HashSet[A], key: A) : bool {. inline .} =
|
||||
if isEmpty(s.data[i].hcode): # end of collision cluster; So all done
|
||||
return
|
||||
r = s.data[i].hcode and msk # "home" location of key@i
|
||||
shallowCopy(s.data[j], s.data[i]) # data[j] will be marked EMPTY next loop
|
||||
shallowCopy(s.data[j], s.data[i]) # data[i] will be marked EMPTY next loop
|
||||
|
||||
proc missingOrExcl*[A](s: var HashSet[A], key: A): bool =
|
||||
## Excludes `key` in the set `s` and tells if `key` was removed from `s`.
|
||||
@@ -662,9 +660,12 @@ proc card*[A](s: OrderedSet[A]): int {.inline.} =
|
||||
|
||||
template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} =
|
||||
var h = s.first
|
||||
var idx = 0
|
||||
while h >= 0:
|
||||
var nxt = s.data[h].next
|
||||
if isFilled(s.data[h].hcode): yieldStmt
|
||||
if isFilled(s.data[h].hcode):
|
||||
yieldStmt
|
||||
inc(idx)
|
||||
h = nxt
|
||||
|
||||
iterator items*[A](s: OrderedSet[A]): A =
|
||||
@@ -689,6 +690,11 @@ iterator items*[A](s: OrderedSet[A]): A =
|
||||
forAllOrderedPairs:
|
||||
yield s.data[h].key
|
||||
|
||||
iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
|
||||
assert s.isValid, "The set needs to be initialized"
|
||||
forAllOrderedPairs:
|
||||
yield (idx, s.data[h].key)
|
||||
|
||||
proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: Hash): int {.inline.} =
|
||||
rawGetKnownHCImpl()
|
||||
|
||||
@@ -760,6 +766,67 @@ proc incl*[A](s: var HashSet[A], other: OrderedSet[A]) =
|
||||
assert other.isValid, "The set `other` needs to be initialized."
|
||||
for item in other: incl(s, item)
|
||||
|
||||
proc exclImpl[A](s: var OrderedSet[A], key: A) : bool {. inline .} =
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
var hc: Hash
|
||||
var i = rawGet(s, key, hc)
|
||||
var msk = high(s.data)
|
||||
result = true
|
||||
|
||||
if i >= 0:
|
||||
result = false
|
||||
# Fix ordering
|
||||
if s.first == i:
|
||||
s.first = s.data[i].next
|
||||
else:
|
||||
var itr = s.first
|
||||
while true:
|
||||
if (s.data[itr].next == i):
|
||||
s.data[itr].next = s.data[i].next
|
||||
if s.last == i:
|
||||
s.last = itr
|
||||
break
|
||||
itr = s.data[itr].next
|
||||
|
||||
dec(s.counter)
|
||||
while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
|
||||
var j = i # The correctness of this depends on (h+1) in nextTry,
|
||||
var r = j # though may be adaptable to other simple sequences.
|
||||
s.data[i].hcode = 0 # mark current EMPTY
|
||||
s.data[i].key = default(type(s.data[i].key))
|
||||
s.data[i].next = 0
|
||||
doWhile((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
|
||||
i = (i + 1) and msk # increment mod table size
|
||||
if isEmpty(s.data[i].hcode): # end of collision cluster; So all done
|
||||
return
|
||||
r = s.data[i].hcode and msk # "home" location of key@i
|
||||
shallowCopy(s.data[j], s.data[i]) # data[i] will be marked EMPTY next loop
|
||||
|
||||
proc missingOrExcl*[A](s: var OrderedSet[A], key: A): bool =
|
||||
## Excludes `key` in the set `s` and tells if `key` was removed from `s`. Efficiency: O(n).
|
||||
##
|
||||
## The difference with regards to the `excl() <#excl,TOrderedSet[A],A>`_ proc is
|
||||
## that this proc returns `true` if `key` was not present in `s`. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var s = toOrderedSet([2, 3, 6, 7])
|
||||
## assert s.missingOrExcl(4) == true
|
||||
## assert s.missingOrExcl(6) == false
|
||||
exclImpl(s, key)
|
||||
|
||||
|
||||
proc excl*[A](s: var OrderedSet[A], key: A) =
|
||||
## Excludes `key` from the set `s`. Efficiency: O(n).
|
||||
##
|
||||
## This doesn't do anything if `key` is not found in `s`. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## var s = toOrderedSet([2, 3, 6, 7])
|
||||
## s.excl(2)
|
||||
## s.excl(2)
|
||||
## assert s.len == 3
|
||||
discard exclImpl(s, key)
|
||||
|
||||
proc containsOrIncl*[A](s: var OrderedSet[A], key: A): bool =
|
||||
## Includes `key` in the set `s` and tells if `key` was added to `s`.
|
||||
##
|
||||
@@ -986,6 +1053,24 @@ when isMainModule and not defined(release):
|
||||
assert a.len == b.card
|
||||
assert a.len == 2
|
||||
|
||||
block setPairsIterator:
|
||||
var s = toOrderedSet([1, 3, 5, 7])
|
||||
var items = newSeq[tuple[a: int, b: int]]()
|
||||
for idx, item in s: items.add((idx, item))
|
||||
assert items == @[(0, 1), (1, 3), (2, 5), (3, 7)]
|
||||
|
||||
block exclusions:
|
||||
var s = toOrderedSet([1, 2, 3, 6, 7, 4])
|
||||
|
||||
s.excl(3)
|
||||
s.excl(3)
|
||||
s.excl(1)
|
||||
s.excl(4)
|
||||
|
||||
var items = newSeq[int]()
|
||||
for item in s: items.add item
|
||||
assert items == @[2, 6, 7]
|
||||
|
||||
#block orderedSetIterator:
|
||||
# var a = initOrderedSet[int]()
|
||||
# for value in [9, 2, 1, 5, 1, 8, 4, 2]:
|
||||
@@ -1030,6 +1115,11 @@ when isMainModule and not defined(release):
|
||||
if s <= i or mustRehash(s, i):
|
||||
echo "performance issue: rightSize() will not elide enlarge() at ", i
|
||||
|
||||
block missingOrExcl:
|
||||
var s = toOrderedSet([2, 3, 6, 7])
|
||||
assert s.missingOrExcl(4) == true
|
||||
assert s.missingOrExcl(6) == false
|
||||
|
||||
when not defined(testing):
|
||||
echo "Micro tests run successfully."
|
||||
|
||||
|
||||
@@ -721,6 +721,16 @@ proc getElems*(n: JsonNode, default: seq[JsonNode] = @[]): seq[JsonNode] =
|
||||
if n.isNil or n.kind != JArray: return default
|
||||
else: return n.elems
|
||||
|
||||
proc add*(father, child: JsonNode) =
|
||||
## Adds `child` to a JArray node `father`.
|
||||
assert father.kind == JArray
|
||||
father.elems.add(child)
|
||||
|
||||
proc add*(obj: JsonNode, key: string, val: JsonNode) =
|
||||
## Sets a field from a `JObject`.
|
||||
assert obj.kind == JObject
|
||||
obj.fields[key] = val
|
||||
|
||||
proc `%`*(s: string): JsonNode =
|
||||
## Generic constructor for JSON data. Creates a new `JString JsonNode`.
|
||||
new(result)
|
||||
@@ -759,6 +769,19 @@ proc `%`*[T](elements: openArray[T]): JsonNode =
|
||||
result = newJArray()
|
||||
for elem in elements: result.add(%elem)
|
||||
|
||||
when false:
|
||||
# For 'consistency' we could do this, but that only pushes people further
|
||||
# into that evil comfort zone where they can use Nim without understanding it
|
||||
# causing problems later on.
|
||||
proc `%`*(elements: set[bool]): JsonNode =
|
||||
## Generic constructor for JSON data. Creates a new `JObject JsonNode`.
|
||||
## This can only be used with the empty set ``{}`` and is supported
|
||||
## to prevent the gotcha ``%*{}`` which used to produce an empty
|
||||
## JSON array.
|
||||
result = newJObject()
|
||||
assert false notin elements, "usage error: only empty sets allowed"
|
||||
assert true notin elements, "usage error: only empty sets allowed"
|
||||
|
||||
proc `%`*(o: object): JsonNode =
|
||||
## Generic constructor for JSON data. Creates a new `JObject JsonNode`
|
||||
result = newJObject()
|
||||
@@ -779,27 +802,25 @@ proc `%`*(o: enum): JsonNode =
|
||||
proc toJson(x: NimNode): NimNode {.compiletime.} =
|
||||
case x.kind
|
||||
of nnkBracket: # array
|
||||
if x.len == 0: return newCall(bindSym"newJArray")
|
||||
result = newNimNode(nnkBracket)
|
||||
for i in 0 .. <x.len:
|
||||
result.add(toJson(x[i]))
|
||||
|
||||
result = newCall(bindSym"%", result)
|
||||
of nnkTableConstr: # object
|
||||
if x.len == 0: return newCall(bindSym"newJObject")
|
||||
result = newNimNode(nnkTableConstr)
|
||||
for i in 0 .. <x.len:
|
||||
x[i].expectKind nnkExprColonExpr
|
||||
result.add(newNimNode(nnkExprColonExpr).add(x[i][0]).add(toJson(x[i][1])))
|
||||
|
||||
result.add newTree(nnkExprColonExpr, x[i][0], toJson(x[i][1]))
|
||||
result = newCall(bindSym"%", result)
|
||||
of nnkCurly: # empty object
|
||||
result = newNimNode(nnkTableConstr)
|
||||
x.expectLen(0)
|
||||
|
||||
result = newCall(bindSym"newJObject")
|
||||
of nnkNilLit:
|
||||
result = newCall("newJNull")
|
||||
|
||||
result = newCall(bindSym"newJNull")
|
||||
else:
|
||||
result = x
|
||||
|
||||
result = prefix(result, "%")
|
||||
result = newCall(bindSym"%", x)
|
||||
|
||||
macro `%*`*(x: untyped): untyped =
|
||||
## Convert an expression to a JsonNode directly, without having to specify
|
||||
@@ -909,16 +930,6 @@ proc contains*(node: JsonNode, val: JsonNode): bool =
|
||||
proc existsKey*(node: JsonNode, key: string): bool {.deprecated.} = node.hasKey(key)
|
||||
## Deprecated for `hasKey`
|
||||
|
||||
proc add*(father, child: JsonNode) =
|
||||
## Adds `child` to a JArray node `father`.
|
||||
assert father.kind == JArray
|
||||
father.elems.add(child)
|
||||
|
||||
proc add*(obj: JsonNode, key: string, val: JsonNode) =
|
||||
## Sets a field from a `JObject`.
|
||||
assert obj.kind == JObject
|
||||
obj.fields[key] = val
|
||||
|
||||
proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} =
|
||||
## Sets a field from a `JObject`.
|
||||
assert(obj.kind == JObject)
|
||||
@@ -1203,7 +1214,7 @@ proc parseJson(p: var JsonParser): JsonNode =
|
||||
raiseParseErr(p, "{")
|
||||
|
||||
when not defined(js):
|
||||
proc parseJson*(s: Stream, filename: string): JsonNode =
|
||||
proc parseJson*(s: Stream, filename: string = ""): JsonNode =
|
||||
## Parses from a stream `s` into a `JsonNode`. `filename` is only needed
|
||||
## for nice error messages.
|
||||
## If `s` contains extra data, it will raise `JsonParsingError`.
|
||||
@@ -1934,4 +1945,8 @@ when isMainModule:
|
||||
except JsonParsingError:
|
||||
doAssert getCurrentExceptionMsg().contains(errorMessages[errEofExpected])
|
||||
|
||||
# bug #6438
|
||||
doAssert($ %*[] == "[]")
|
||||
doAssert($ %*{} == "{}")
|
||||
|
||||
echo("Tests succeeded!")
|
||||
|
||||
@@ -498,7 +498,7 @@ proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
|
||||
# Cannot use INET6_ADDRSTRLEN here, because it's a C define.
|
||||
var buf: array[64, char]
|
||||
if inet_ntop(name.sin6_family.cint,
|
||||
addr name, buf.cstring, sizeof(buf).int32).isNil:
|
||||
addr name.sin6_addr, buf.cstring, sizeof(buf).int32).isNil:
|
||||
raiseOSError(osLastError())
|
||||
result = ($buf, Port(nativesockets.ntohs(name.sin6_port)))
|
||||
else:
|
||||
@@ -534,7 +534,7 @@ proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
|
||||
# Cannot use INET6_ADDRSTRLEN here, because it's a C define.
|
||||
var buf: array[64, char]
|
||||
if inet_ntop(name.sin6_family.cint,
|
||||
addr name, buf.cstring, sizeof(buf).int32).isNil:
|
||||
addr name.sin6_addr, buf.cstring, sizeof(buf).int32).isNil:
|
||||
raiseOSError(osLastError())
|
||||
result = ($buf, Port(nativesockets.ntohs(name.sin6_port)))
|
||||
else:
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
## A value of type ``Option[T]`` either contains a value `x` (represented as
|
||||
## ``some(x)``) or is empty (``none(T)``).
|
||||
##
|
||||
## This can be useful when you have a value that can be present or not. The
|
||||
## This can be useful when you have a value that can be present or not. The
|
||||
## absence of a value is often represented by ``nil``, but it is not always
|
||||
## available, nor is it always a good solution.
|
||||
##
|
||||
@@ -67,10 +67,8 @@
|
||||
## assert(false) # This will not be reached
|
||||
## except UnpackError: # Because an exception is raised
|
||||
## discard
|
||||
|
||||
import typetraits
|
||||
|
||||
|
||||
type
|
||||
Option*[T] = object
|
||||
## An optional type that stores its value and state separately in a boolean.
|
||||
@@ -78,7 +76,6 @@ type
|
||||
has: bool
|
||||
UnpackError* = ref object of ValueError
|
||||
|
||||
|
||||
proc some*[T](val: T): Option[T] =
|
||||
## Returns a ``Option`` that has this value.
|
||||
result.has = true
|
||||
@@ -88,14 +85,12 @@ proc none*(T: typedesc): Option[T] =
|
||||
## Returns a ``Option`` for this type that has no value.
|
||||
result.has = false
|
||||
|
||||
|
||||
proc isSome*[T](self: Option[T]): bool =
|
||||
self.has
|
||||
|
||||
proc isNone*[T](self: Option[T]): bool =
|
||||
not self.has
|
||||
|
||||
|
||||
proc unsafeGet*[T](self: Option[T]): T =
|
||||
## Returns the value of a ``some``. Behavior is undefined for ``none``.
|
||||
assert self.isSome
|
||||
@@ -110,12 +105,11 @@ proc get*[T](self: Option[T]): T =
|
||||
|
||||
proc get*[T](self: Option[T], otherwise: T): T =
|
||||
## Returns the contents of this option or `otherwise` if the option is none.
|
||||
if self.isSome:
|
||||
if self.has:
|
||||
self.val
|
||||
else:
|
||||
otherwise
|
||||
|
||||
|
||||
proc map*[T](self: Option[T], callback: proc (input: T)) =
|
||||
## Applies a callback to the value in this Option
|
||||
if self.has:
|
||||
@@ -123,12 +117,27 @@ proc map*[T](self: Option[T], callback: proc (input: T)) =
|
||||
|
||||
proc map*[T, R](self: Option[T], callback: proc (input: T): R): Option[R] =
|
||||
## Applies a callback to the value in this Option and returns an option
|
||||
## containing the new value. If this option is None, None will be returned
|
||||
## containing the new value. If this option is None, None will be returned.
|
||||
if self.has:
|
||||
some[R]( callback(self.val) )
|
||||
some[R](callback(self.val))
|
||||
else:
|
||||
none(R)
|
||||
|
||||
proc flatten*[A](self: Option[Option[A]]): Option[A] =
|
||||
## Remove one level of structure in a nested Option.
|
||||
if self.has:
|
||||
self.val
|
||||
else:
|
||||
none(A)
|
||||
|
||||
proc flatMap*[A, B](self: Option[A], callback: proc (input: A): Option[B]): Option[B] =
|
||||
## Applies a callback to the value in this Option and returns an
|
||||
## option containing the new value. If this option is None, None will be
|
||||
## returned. Similar to ``map``, with the difference that the callback
|
||||
## returns an Option, not a raw value. This allows multiple procs with a
|
||||
## signature of ``A -> Option[B]`` (including A = B) to be chained together.
|
||||
map(self, callback).flatten()
|
||||
|
||||
proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] =
|
||||
## Applies a callback to the value in this Option. If the callback returns
|
||||
## `true`, the option is returned as a Some. If it returns false, it is
|
||||
@@ -138,21 +147,18 @@ proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] =
|
||||
else:
|
||||
self
|
||||
|
||||
|
||||
proc `==`*(a, b: Option): bool =
|
||||
## Returns ``true`` if both ``Option``s are ``none``,
|
||||
## or if they have equal values
|
||||
(a.has and b.has and a.val == b.val) or (not a.has and not b.has)
|
||||
|
||||
|
||||
proc `$`*[T]( self: Option[T] ): string =
|
||||
proc `$`*[T](self: Option[T]): string =
|
||||
## Returns the contents of this option or `otherwise` if the option is none.
|
||||
if self.has:
|
||||
"Some(" & $self.val & ")"
|
||||
else:
|
||||
"None[" & T.name & "]"
|
||||
|
||||
|
||||
when isMainModule:
|
||||
import unittest, sequtils
|
||||
|
||||
@@ -198,12 +204,12 @@ when isMainModule:
|
||||
check false
|
||||
|
||||
test "get with a default value":
|
||||
check( some("Correct").get("Wrong") == "Correct" )
|
||||
check( stringNone.get("Correct") == "Correct" )
|
||||
check(some("Correct").get("Wrong") == "Correct")
|
||||
check(stringNone.get("Correct") == "Correct")
|
||||
|
||||
test "$":
|
||||
check( $(some("Correct")) == "Some(Correct)" )
|
||||
check( $(stringNone) == "None[string]" )
|
||||
check($(some("Correct")) == "Some(Correct)")
|
||||
check($(stringNone) == "None[string]")
|
||||
|
||||
test "map with a void result":
|
||||
var procRan = 0
|
||||
@@ -212,11 +218,38 @@ when isMainModule:
|
||||
intNone.map(proc (v: int) = check false)
|
||||
|
||||
test "map":
|
||||
check( some(123).map(proc (v: int): int = v * 2) == some(246) )
|
||||
check( intNone.map(proc (v: int): int = v * 2).isNone )
|
||||
check(some(123).map(proc (v: int): int = v * 2) == some(246))
|
||||
check(intNone.map(proc (v: int): int = v * 2).isNone)
|
||||
|
||||
test "filter":
|
||||
check( some(123).filter(proc (v: int): bool = v == 123) == some(123) )
|
||||
check( some(456).filter(proc (v: int): bool = v == 123).isNone )
|
||||
check( intNone.filter(proc (v: int): bool = check false).isNone )
|
||||
check(some(123).filter(proc (v: int): bool = v == 123) == some(123))
|
||||
check(some(456).filter(proc (v: int): bool = v == 123).isNone)
|
||||
check(intNone.filter(proc (v: int): bool = check false).isNone)
|
||||
|
||||
test "flatMap":
|
||||
proc addOneIfNotZero(v: int): Option[int] =
|
||||
if v != 0:
|
||||
result = some(v + 1)
|
||||
else:
|
||||
result = none(int)
|
||||
|
||||
check(some(1).flatMap(addOneIfNotZero) == some(2))
|
||||
check(some(0).flatMap(addOneIfNotZero) == none(int))
|
||||
check(some(1).flatMap(addOneIfNotZero).flatMap(addOneIfNotZero) == some(3))
|
||||
|
||||
proc maybeToString(v: int): Option[string] =
|
||||
if v != 0:
|
||||
result = some($v)
|
||||
else:
|
||||
result = none(string)
|
||||
|
||||
check(some(1).flatMap(maybeToString) == some("1"))
|
||||
|
||||
proc maybeExclaim(v: string): Option[string] =
|
||||
if v != "":
|
||||
result = some v & "!"
|
||||
else:
|
||||
result = none(string)
|
||||
|
||||
check(some(1).flatMap(maybeToString).flatMap(maybeExclaim) == some("1!"))
|
||||
check(some(0).flatMap(maybeToString).flatMap(maybeExclaim) == none(string))
|
||||
|
||||
@@ -410,13 +410,11 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
result.readDataImpl = hsReadData
|
||||
result.writeDataImpl = hsWriteData
|
||||
|
||||
proc buildCommandLine(a: string, args: openArray[string]): cstring =
|
||||
var res = quoteShell(a)
|
||||
proc buildCommandLine(a: string, args: openArray[string]): string =
|
||||
result = quoteShell(a)
|
||||
for i in 0..high(args):
|
||||
res.add(' ')
|
||||
res.add(quoteShell(args[i]))
|
||||
result = cast[cstring](alloc0(res.len+1))
|
||||
copyMem(result, cstring(res), res.len)
|
||||
result.add(' ')
|
||||
result.add(quoteShell(args[i]))
|
||||
|
||||
proc buildEnv(env: StringTableRef): tuple[str: cstring, len: int] =
|
||||
var L = 0
|
||||
@@ -540,11 +538,13 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
result.errHandle = FileHandle(si.hStdError)
|
||||
|
||||
var cmdl: cstring
|
||||
var cmdRoot: string
|
||||
if poEvalCommand in options:
|
||||
cmdl = command
|
||||
assert args.len == 0
|
||||
else:
|
||||
cmdl = buildCommandLine(command, args)
|
||||
cmdRoot = buildCommandLine(command, args)
|
||||
cmdl = cstring(cmdRoot)
|
||||
var wd: cstring = nil
|
||||
var e = (str: nil.cstring, len: -1)
|
||||
if len(workingDir) > 0: wd = workingDir
|
||||
|
||||
@@ -888,7 +888,7 @@ proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect,
|
||||
n = x
|
||||
result = newString(len)
|
||||
for j in countdown(len-1, 0):
|
||||
result[j] = HexChars[(n and 0xF).int]
|
||||
result[j] = HexChars[int(n and 0xF)]
|
||||
n = n shr 4
|
||||
# handle negative overflow
|
||||
if n == 0 and x < 0: n = -1
|
||||
|
||||
@@ -509,10 +509,6 @@ macro check*(conditions: untyped): untyped =
|
||||
## "AKB48".toLowerAscii() == "akb48"
|
||||
## 'C' in teams
|
||||
let checked = callsite()[1]
|
||||
var
|
||||
argsAsgns = newNimNode(nnkStmtList)
|
||||
argsPrintOuts = newNimNode(nnkStmtList)
|
||||
counter = 0
|
||||
|
||||
template asgn(a: untyped, value: typed) =
|
||||
var a = value # XXX: we need "var: var" here in order to
|
||||
@@ -522,66 +518,71 @@ macro check*(conditions: untyped): untyped =
|
||||
when compiles(string($value)):
|
||||
checkpoint(name & " was " & $value)
|
||||
|
||||
proc inspectArgs(exp: NimNode): NimNode =
|
||||
result = copyNimTree(exp)
|
||||
proc inspectArgs(exp: NimNode): tuple[assigns, check, printOuts: NimNode] =
|
||||
result.check = copyNimTree(exp)
|
||||
result.assigns = newNimNode(nnkStmtList)
|
||||
result.printOuts = newNimNode(nnkStmtList)
|
||||
|
||||
var counter = 0
|
||||
|
||||
if exp[0].kind == nnkIdent and
|
||||
$exp[0] in ["and", "or", "not", "in", "notin", "==", "<=",
|
||||
$exp[0] in ["not", "in", "notin", "==", "<=",
|
||||
">=", "<", ">", "!=", "is", "isnot"]:
|
||||
for i in countup(1, exp.len - 1):
|
||||
|
||||
for i in 1 ..< exp.len:
|
||||
if exp[i].kind notin nnkLiterals:
|
||||
inc counter
|
||||
var arg = newIdentNode(":p" & $counter)
|
||||
var argStr = exp[i].toStrLit
|
||||
var paramAst = exp[i]
|
||||
let argStr = exp[i].toStrLit
|
||||
let paramAst = exp[i]
|
||||
if exp[i].kind == nnkIdent:
|
||||
argsPrintOuts.add getAst(print(argStr, paramAst))
|
||||
if exp[i].kind in nnkCallKinds:
|
||||
var callVar = newIdentNode(":c" & $counter)
|
||||
argsAsgns.add getAst(asgn(callVar, paramAst))
|
||||
result[i] = callVar
|
||||
argsPrintOuts.add getAst(print(argStr, callVar))
|
||||
result.printOuts.add getAst(print(argStr, paramAst))
|
||||
if exp[i].kind in nnkCallKinds + { nnkDotExpr, nnkBracketExpr }:
|
||||
let callVar = newIdentNode(":c" & $counter)
|
||||
result.assigns.add getAst(asgn(callVar, paramAst))
|
||||
result.check[i] = callVar
|
||||
result.printOuts.add getAst(print(argStr, callVar))
|
||||
if exp[i].kind == nnkExprEqExpr:
|
||||
# ExprEqExpr
|
||||
# Ident !"v"
|
||||
# IntLit 2
|
||||
result[i] = exp[i][1]
|
||||
result.check[i] = exp[i][1]
|
||||
if exp[i].typekind notin {ntyTypeDesc}:
|
||||
argsAsgns.add getAst(asgn(arg, paramAst))
|
||||
argsPrintOuts.add getAst(print(argStr, arg))
|
||||
let arg = newIdentNode(":p" & $counter)
|
||||
result.assigns.add getAst(asgn(arg, paramAst))
|
||||
result.printOuts.add getAst(print(argStr, arg))
|
||||
if exp[i].kind != nnkExprEqExpr:
|
||||
result[i] = arg
|
||||
result.check[i] = arg
|
||||
else:
|
||||
result[i][1] = arg
|
||||
result.check[i][1] = arg
|
||||
|
||||
case checked.kind
|
||||
of nnkCallKinds:
|
||||
template rewrite(call, lineInfoLit, callLit,
|
||||
argAssgs, argPrintOuts) =
|
||||
block:
|
||||
argAssgs #all callables (and assignments) are run here
|
||||
if not call:
|
||||
checkpoint(lineInfoLit & ": Check failed: " & callLit)
|
||||
argPrintOuts
|
||||
fail()
|
||||
|
||||
var checkedStr = checked.toStrLit
|
||||
let parameterizedCheck = inspectArgs(checked)
|
||||
result = getAst(rewrite(parameterizedCheck, checked.lineinfo, checkedStr,
|
||||
argsAsgns, argsPrintOuts))
|
||||
let (assigns, check, printOuts) = inspectArgs(checked)
|
||||
let lineinfo = newStrLitNode(checked.lineinfo)
|
||||
let callLit = checked.toStrLit
|
||||
result = quote do:
|
||||
block:
|
||||
`assigns`
|
||||
if not `check`:
|
||||
checkpoint(`lineinfo` & ": Check failed: " & `callLit`)
|
||||
`printOuts`
|
||||
fail()
|
||||
|
||||
of nnkStmtList:
|
||||
result = newNimNode(nnkStmtList)
|
||||
for i in countup(0, checked.len - 1):
|
||||
if checked[i].kind != nnkCommentStmt:
|
||||
result.add(newCall(!"check", checked[i]))
|
||||
for node in checked:
|
||||
if node.kind != nnkCommentStmt:
|
||||
result.add(newCall(!"check", node))
|
||||
|
||||
else:
|
||||
template rewrite(exp, lineInfoLit, expLit) =
|
||||
if not exp:
|
||||
checkpoint(lineInfoLit & ": Check failed: " & expLit)
|
||||
fail()
|
||||
let lineinfo = newStrLitNode(checked.lineinfo)
|
||||
let callLit = checked.toStrLit
|
||||
|
||||
result = getAst(rewrite(checked, checked.lineinfo, checked.toStrLit))
|
||||
result = quote do:
|
||||
if not `checked`:
|
||||
checkpoint(`lineinfo` & ": Check failed: " & `callLit`)
|
||||
fail()
|
||||
|
||||
template require*(conditions: untyped) =
|
||||
## Same as `check` except any failed test causes the program to quit
|
||||
|
||||
@@ -246,6 +246,9 @@ type
|
||||
UncheckedArray* {.unchecked.}[T] = array[0, T]
|
||||
## Array with no bounds checking
|
||||
|
||||
when defined(nimHasOpt):
|
||||
type opt*{.magic: "Opt".}[T]
|
||||
|
||||
proc high*[T: Ordinal](x: T): T {.magic: "High", noSideEffect.}
|
||||
## returns the highest possible index of an array, a sequence, a string or
|
||||
## the highest possible value of an ordinal value `x`. As a special
|
||||
@@ -409,8 +412,7 @@ when not defined(JS):
|
||||
|
||||
when not defined(JS) and not defined(nimscript):
|
||||
template space(s: PGenericSeq): int {.dirty.} =
|
||||
s.reserved and not seqShallowFlag
|
||||
|
||||
s.reserved and not (seqShallowFlag or strlitFlag)
|
||||
include "system/hti"
|
||||
|
||||
type
|
||||
@@ -718,7 +720,7 @@ proc len*[TOpenArray: openArray|varargs](x: TOpenArray): int {.
|
||||
magic: "LengthOpenArray", noSideEffect.}
|
||||
proc len*(x: string): int {.magic: "LengthStr", noSideEffect.}
|
||||
proc len*(x: cstring): int {.magic: "LengthStr", noSideEffect.}
|
||||
proc len*[I, T](x: array[I, T]): int {.magic: "LengthArray", noSideEffect.}
|
||||
proc len*(x: (type array)|array): int {.magic: "LengthArray", noSideEffect.}
|
||||
proc len*[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.}
|
||||
## returns the length of an array, an openarray, a sequence or a string.
|
||||
## This is roughly the same as ``high(T)-low(T)+1``, but its resulting type is
|
||||
@@ -1329,6 +1331,9 @@ const
|
||||
## "amd64", "mips", "mipsel", "arm", "arm64", "mips64", "mips64el".
|
||||
|
||||
seqShallowFlag = low(int)
|
||||
strlitFlag = 1 shl (sizeof(int)*8 - 2) # later versions of the codegen \
|
||||
# emit this flag
|
||||
# for string literals, it allows for some optimizations.
|
||||
|
||||
{.push profiler: off.}
|
||||
when defined(nimKnowsNimvm):
|
||||
@@ -1435,7 +1440,12 @@ when defined(nimdoc):
|
||||
|
||||
elif defined(genode):
|
||||
proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn,
|
||||
importcpp: "genodeEnv->parent().exit(@)", header: "<base/env.h>".}
|
||||
importcpp: "genodeEnv->parent().exit(@); Genode::sleep_forever()",
|
||||
header: "<base/sleep.h>".}
|
||||
|
||||
elif defined(nodejs):
|
||||
proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit",
|
||||
importc: "process.exit", noreturn.}
|
||||
|
||||
else:
|
||||
proc quit*(errorcode: int = QuitSuccess) {.
|
||||
@@ -3402,10 +3412,10 @@ when hasAlloc or defined(nimscript):
|
||||
proc `[]`*(s: string, x: Slice[int]): string {.inline.} =
|
||||
## slice operation for strings.
|
||||
## returns the inclusive range [s[x.a], s[x.b]]:
|
||||
##
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var s = "abcdef"
|
||||
## assert s[1..3] == "bcd"
|
||||
## assert s[1..3] == "bcd"
|
||||
result = s.substr(x.a, x.b)
|
||||
|
||||
proc `[]=`*(s: var string, x: Slice[int], b: string) =
|
||||
@@ -3427,7 +3437,7 @@ when hasAlloc or defined(nimscript):
|
||||
proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[int]): seq[T] =
|
||||
## slice operation for arrays.
|
||||
## returns the inclusive range [a[x.a], a[x.b]]:
|
||||
##
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var a = [1,2,3,4]
|
||||
## assert a[0..2] == @[1,2,3]
|
||||
@@ -3466,7 +3476,7 @@ proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[Idx], b: openArray[T]) =
|
||||
proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] =
|
||||
## slice operation for sequences.
|
||||
## returns the inclusive range [s[x.a], s[x.b]]:
|
||||
##
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var s = @[1,2,3,4]
|
||||
## assert s[0..2] == @[1,2,3]
|
||||
@@ -3719,7 +3729,9 @@ proc shallow*(s: var string) {.noSideEffect, inline.} =
|
||||
## purposes.
|
||||
when not defined(JS) and not defined(nimscript):
|
||||
var s = cast[PGenericSeq](s)
|
||||
s.reserved = s.reserved or seqShallowFlag
|
||||
# string literals cannot become 'shallow':
|
||||
if (s.reserved and strlitFlag) == 0:
|
||||
s.reserved = s.reserved or seqShallowFlag
|
||||
|
||||
type
|
||||
NimNodeObj = object
|
||||
|
||||
@@ -63,12 +63,17 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
|
||||
sysAssert(dest != nil, "genericAssignAux 3")
|
||||
unsureAsgnRef(x, newSeq(mt, seq.len))
|
||||
var dst = cast[ByteAddress](cast[PPointer](dest)[])
|
||||
for i in 0..seq.len-1:
|
||||
genericAssignAux(
|
||||
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
|
||||
cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
|
||||
GenericSeqSize),
|
||||
mt.base, shallow)
|
||||
if ntfNoRefs in mt.base.flags:
|
||||
copyMem(cast[pointer](dst +% GenericSeqSize),
|
||||
cast[pointer](cast[ByteAddress](s2) +% GenericSeqSize),
|
||||
seq.len * mt.base.size)
|
||||
else:
|
||||
for i in 0..seq.len-1:
|
||||
genericAssignAux(
|
||||
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
|
||||
cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
|
||||
GenericSeqSize),
|
||||
mt.base, shallow)
|
||||
of tyObject:
|
||||
if mt.base != nil:
|
||||
genericAssignAux(dest, src, mt.base, shallow)
|
||||
@@ -89,6 +94,19 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
|
||||
cast[pointer](s +% i*% mt.base.size), mt.base, shallow)
|
||||
of tyRef:
|
||||
unsureAsgnRef(cast[PPointer](dest), cast[PPointer](s)[])
|
||||
of tyOptAsRef:
|
||||
let s2 = cast[PPointer](src)[]
|
||||
let d = cast[PPointer](dest)
|
||||
if s2 == nil:
|
||||
unsureAsgnRef(d, s2)
|
||||
else:
|
||||
when declared(usrToCell):
|
||||
let realType = usrToCell(s2).typ
|
||||
else:
|
||||
let realType = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[]
|
||||
else: mt.base
|
||||
var z = newObj(realType, realType.base.size)
|
||||
genericAssignAux(d, addr z, mt.base, shallow)
|
||||
else:
|
||||
copyMem(dest, src, mt.size) # copy raw bits
|
||||
|
||||
@@ -115,6 +133,7 @@ when false:
|
||||
of tyPtr: k = "ptr"
|
||||
of tyRef: k = "ref"
|
||||
of tyVar: k = "var"
|
||||
of tyOptAsRef: k = "optref"
|
||||
of tySequence: k = "seq"
|
||||
of tyProc: k = "proc"
|
||||
of tyPointer: k = "range"
|
||||
@@ -195,7 +214,7 @@ proc genericReset(dest: pointer, mt: PNimType) =
|
||||
var d = cast[ByteAddress](dest)
|
||||
sysAssert(mt != nil, "genericReset 2")
|
||||
case mt.kind
|
||||
of tyString, tyRef, tySequence:
|
||||
of tyString, tyRef, tyOptAsRef, tySequence:
|
||||
unsureAsgnRef(cast[PPointer](dest), nil)
|
||||
of tyTuple:
|
||||
genericResetAux(dest, mt.node)
|
||||
|
||||
@@ -144,7 +144,7 @@ proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
|
||||
for i in 0..(mt.size div mt.base.size)-1:
|
||||
storeAux(cast[pointer](d +% i*% mt.base.size),
|
||||
cast[pointer](s +% i*% mt.base.size), mt.base, t, mode)
|
||||
of tyRef:
|
||||
of tyRef, tyOptAsRef:
|
||||
var s = cast[PPointer](src)[]
|
||||
var x = cast[PPointer](dest)
|
||||
if s == nil:
|
||||
|
||||
@@ -124,7 +124,7 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) =
|
||||
for i in 0..(mt.size div mt.base.size)-1:
|
||||
genericDeepCopyAux(cast[pointer](d +% i*% mt.base.size),
|
||||
cast[pointer](s +% i*% mt.base.size), mt.base, tab)
|
||||
of tyRef:
|
||||
of tyRef, tyOptAsRef:
|
||||
let s2 = cast[PPointer](src)[]
|
||||
if s2 == nil:
|
||||
unsureAsgnRef(cast[PPointer](dest), s2)
|
||||
|
||||
@@ -349,7 +349,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
|
||||
for i in 0..n.len-1:
|
||||
# inlined for speed
|
||||
if n.sons[i].kind == nkSlot:
|
||||
if n.sons[i].typ.kind in {tyRef, tyString, tySequence}:
|
||||
if n.sons[i].typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}:
|
||||
doOperation(cast[PPointer](d +% n.sons[i].offset)[], op)
|
||||
else:
|
||||
forAllChildrenAux(cast[pointer](d +% n.sons[i].offset),
|
||||
@@ -366,7 +366,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
|
||||
if dest == nil: return # nothing to do
|
||||
if ntfNoRefs notin mt.flags:
|
||||
case mt.kind
|
||||
of tyRef, tyString, tySequence: # leaf:
|
||||
of tyRef, tyOptAsRef, tyString, tySequence: # leaf:
|
||||
doOperation(cast[PPointer](d)[], op)
|
||||
of tyObject, tyTuple:
|
||||
forAllSlotsAux(dest, mt.node, op)
|
||||
@@ -379,13 +379,13 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
|
||||
gcAssert(cell != nil, "forAllChildren: 1")
|
||||
gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2")
|
||||
gcAssert(cell.typ != nil, "forAllChildren: 3")
|
||||
gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 4"
|
||||
gcAssert cell.typ.kind in {tyRef, tyOptAsRef, tySequence, tyString}, "forAllChildren: 4"
|
||||
let marker = cell.typ.marker
|
||||
if marker != nil:
|
||||
marker(cellToUsr(cell), op.int)
|
||||
else:
|
||||
case cell.typ.kind
|
||||
of tyRef: # common case
|
||||
of tyRef, tyOptAsRef: # common case
|
||||
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
|
||||
of tySequence:
|
||||
var d = cast[ByteAddress](cellToUsr(cell))
|
||||
@@ -461,7 +461,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
|
||||
incTypeSize typ, size
|
||||
sysAssert(allocInv(gch.region), "rawNewObj begin")
|
||||
acquire(gch)
|
||||
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
|
||||
gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
|
||||
collectCT(gch)
|
||||
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
|
||||
#gcAssert typ.kind in {tyString, tySequence} or size >= typ.base.size, "size too small"
|
||||
@@ -509,7 +509,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
incTypeSize typ, size
|
||||
sysAssert(allocInv(gch.region), "newObjRC1 begin")
|
||||
acquire(gch)
|
||||
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
|
||||
gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
|
||||
collectCT(gch)
|
||||
sysAssert(allocInv(gch.region), "newObjRC1 after collectCT")
|
||||
|
||||
@@ -945,10 +945,10 @@ when not defined(useNimRtl):
|
||||
"[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
|
||||
"[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000) & "\n"
|
||||
when nimCoroutines:
|
||||
result = result & "[GC] number of stacks: " & $gch.stack.len & "\n"
|
||||
result.add "[GC] number of stacks: " & $gch.stack.len & "\n"
|
||||
for stack in items(gch.stack):
|
||||
result = result & "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & cast[pointer](stack.maxStackSize).repr & "\n"
|
||||
result.add "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & cast[pointer](stack.maxStackSize).repr & "\n"
|
||||
else:
|
||||
result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
|
||||
result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
|
||||
|
||||
{.pop.} # profiler: off, stackTrace: off
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
# (c) Copyright 2017 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
@@ -9,17 +9,19 @@
|
||||
|
||||
# Garbage Collector
|
||||
#
|
||||
# The basic algorithm is *Deferred Reference Counting* with an incremental mark
|
||||
# The basic algorithm is an incremental mark
|
||||
# and sweep GC to free cycles. It is hard realtime in that if you play
|
||||
# according to its rules, no deadline will ever be missed.
|
||||
|
||||
# XXX Ensure by smart color masking that the object is not in the ZCT.
|
||||
# Since this kind of collector is very bad at recycling dead objects
|
||||
# early, Nim's codegen emits ``nimEscape`` calls at strategic
|
||||
# places. For this to work even 'unsureAsgnRef' needs to mark things
|
||||
# so that only return values need to be considered in ``nimEscape``.
|
||||
|
||||
{.push profiler:off.}
|
||||
|
||||
const
|
||||
CycleIncrease = 2 # is a multiplicative increase
|
||||
InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow
|
||||
InitialCycleThreshold = 512*1024 # start collecting after 500KB
|
||||
ZctThreshold = 500 # we collect garbage if the ZCT's size
|
||||
# reaches this threshold
|
||||
# this seems to be a good value
|
||||
@@ -40,13 +42,11 @@ type
|
||||
iterToProc(allObjects, ptr ObjectSpaceIter, allObjectsAsProc)
|
||||
|
||||
const
|
||||
rcIncrement = 0b1000 # so that lowest 3 bits are not touched
|
||||
escapedBit = 0b1000 # so that lowest 3 bits are not touched
|
||||
rcBlackOrig = 0b000
|
||||
rcWhiteOrig = 0b001
|
||||
rcGrey = 0b010 # traditional color for incremental mark&sweep
|
||||
rcUnused = 0b011
|
||||
ZctFlag = 0b100 # in ZCT
|
||||
rcShift = 3 # shift by rcShift to get the reference counter
|
||||
colorMask = 0b011
|
||||
type
|
||||
WalkOp = enum
|
||||
@@ -63,13 +63,13 @@ type
|
||||
|
||||
GcStat = object
|
||||
stackScans: int # number of performed stack scans (for statistics)
|
||||
cycleCollections: int # number of performed full collections
|
||||
completedCollections: int # number of performed full collections
|
||||
maxThreshold: int # max threshold that has been set
|
||||
maxStackSize: int # max stack size
|
||||
maxStackCells: int # max stack cells in ``decStack``
|
||||
cycleTableSize: int # max entries in cycle table
|
||||
maxPause: int64 # max measured GC pause in nanoseconds
|
||||
|
||||
|
||||
GcStack {.final, pure.} = object
|
||||
when nimCoroutines:
|
||||
prev: ptr GcStack
|
||||
@@ -93,15 +93,13 @@ type
|
||||
cycleThreshold: int
|
||||
when useCellIds:
|
||||
idGenerator: int
|
||||
zct: CellSeq # the zero count table
|
||||
decStack: CellSeq # cells in the stack that are to decref again
|
||||
greyStack: CellSeq
|
||||
recGcLock: int # prevent recursion via finalizers; no thread lock
|
||||
when withRealTime:
|
||||
maxPause: Nanos # max allowed pause in nanoseconds; active if > 0
|
||||
region: MemRegion # garbage collected region
|
||||
stat: GcStat
|
||||
additionalRoots: CellSeq # dummy roots for GC_ref/unref
|
||||
additionalRoots: CellSeq # explicit roots for GC_ref/unref
|
||||
spaceIter: ObjectSpaceIter
|
||||
pDumpHeapFile: pointer # File that is used for GC_dumpHeap
|
||||
when hasThreadSupport:
|
||||
@@ -113,19 +111,25 @@ var
|
||||
when not defined(useNimRtl):
|
||||
instantiateForRegion(gch.region)
|
||||
|
||||
template acquire(gch: GcHeap) =
|
||||
when hasThreadSupport and hasSharedHeap:
|
||||
acquireSys(HeapLock)
|
||||
|
||||
template release(gch: GcHeap) =
|
||||
when hasThreadSupport and hasSharedHeap:
|
||||
releaseSys(HeapLock)
|
||||
|
||||
proc initGC() =
|
||||
when not defined(useNimRtl):
|
||||
gch.red = (1-gch.black)
|
||||
gch.cycleThreshold = InitialCycleThreshold
|
||||
gch.stat.stackScans = 0
|
||||
gch.stat.cycleCollections = 0
|
||||
gch.stat.completedCollections = 0
|
||||
gch.stat.maxThreshold = 0
|
||||
gch.stat.maxStackSize = 0
|
||||
gch.stat.maxStackCells = 0
|
||||
gch.stat.cycleTableSize = 0
|
||||
# init the rt
|
||||
init(gch.zct)
|
||||
init(gch.decStack)
|
||||
init(gch.additionalRoots)
|
||||
init(gch.greyStack)
|
||||
when hasThreadSupport:
|
||||
@@ -147,11 +151,6 @@ template gcAssert(cond: bool, msg: string) =
|
||||
writeStackTrace()
|
||||
quit 1
|
||||
|
||||
proc addZCT(s: var CellSeq, c: PCell) {.noinline.} =
|
||||
if (c.refcount and ZctFlag) == 0:
|
||||
c.refcount = c.refcount or ZctFlag
|
||||
add(s, c)
|
||||
|
||||
proc cellToUsr(cell: PCell): pointer {.inline.} =
|
||||
# convert object (=pointer to refcount) to pointer to userdata
|
||||
result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell)))
|
||||
@@ -168,7 +167,7 @@ proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
|
||||
result = usrToCell(c).typ
|
||||
|
||||
proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
|
||||
result = int(usrToCell(p).refcount) shr rcShift
|
||||
result = 0
|
||||
|
||||
# this that has to equals zero, otherwise we have to round up UnitsPerPage:
|
||||
when BitsPerPage mod (sizeof(int)*8) != 0:
|
||||
@@ -178,6 +177,12 @@ template color(c): expr = c.refCount and colorMask
|
||||
template setColor(c, col) =
|
||||
c.refcount = c.refcount and not colorMask or col
|
||||
|
||||
template markAsEscaped(c: PCell) =
|
||||
c.refcount = c.refcount or escapedBit
|
||||
|
||||
template didEscape(c: PCell): bool =
|
||||
(c.refCount and escapedBit) != 0
|
||||
|
||||
proc writeCell(file: File; msg: cstring, c: PCell) =
|
||||
var kind = -1
|
||||
if c.typ != nil: kind = ord(c.typ.kind)
|
||||
@@ -189,18 +194,18 @@ proc writeCell(file: File; msg: cstring, c: PCell) =
|
||||
else:
|
||||
let id = c
|
||||
when leakDetector:
|
||||
c_fprintf(file, "%s %p %d rc=%ld color=%c from %s(%ld)\n",
|
||||
msg, id, kind, c.refcount shr rcShift, col, c.filename, c.line)
|
||||
c_fprintf(file, "%s %p %d escaped=%ld color=%c from %s(%ld)\n",
|
||||
msg, id, kind, didEscape(c), col, c.filename, c.line)
|
||||
else:
|
||||
c_fprintf(file, "%s %p %d rc=%ld color=%c\n",
|
||||
msg, id, kind, c.refcount shr rcShift, col)
|
||||
c_fprintf(file, "%s %p %d escaped=%ld color=%c\n",
|
||||
msg, id, kind, didEscape(c), col)
|
||||
|
||||
proc writeCell(msg: cstring, c: PCell) =
|
||||
stdout.writeCell(msg, c)
|
||||
|
||||
proc myastToStr[T](x: T): string {.magic: "AstToStr", noSideEffect.}
|
||||
|
||||
template gcTrace(cell, state: expr): stmt {.immediate.} =
|
||||
template gcTrace(cell, state: untyped) =
|
||||
when traceGC: writeCell(myastToStr(state), cell)
|
||||
|
||||
# forward declarations:
|
||||
@@ -211,52 +216,17 @@ proc doOperation(p: pointer, op: WalkOp) {.benign.}
|
||||
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
|
||||
# we need the prototype here for debugging purposes
|
||||
|
||||
when hasThreadSupport and hasSharedHeap:
|
||||
template `--`(x: expr): expr = atomicDec(x, rcIncrement) <% rcIncrement
|
||||
template `++`(x: expr): stmt = discard atomicInc(x, rcIncrement)
|
||||
else:
|
||||
template `--`(x: expr): expr =
|
||||
dec(x, rcIncrement)
|
||||
x <% rcIncrement
|
||||
template `++`(x: expr): stmt = inc(x, rcIncrement)
|
||||
|
||||
proc prepareDealloc(cell: PCell) =
|
||||
if cell.typ.finalizer != nil:
|
||||
# the finalizer could invoke something that
|
||||
# allocates memory; this could trigger a garbage
|
||||
# collection. Since we are already collecting we
|
||||
# prevend recursive entering here by a lock.
|
||||
# XXX: we should set the cell's children to nil!
|
||||
inc(gch.recGcLock)
|
||||
(cast[Finalizer](cell.typ.finalizer))(cellToUsr(cell))
|
||||
dec(gch.recGcLock)
|
||||
|
||||
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
|
||||
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
||||
discard
|
||||
|
||||
proc rtlAddZCT(c: PCell) {.rtl, inl.} =
|
||||
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
||||
addZCT(gch.zct, c)
|
||||
|
||||
proc decRef(c: PCell) {.inline.} =
|
||||
gcAssert(isAllocatedPtr(gch.region, c), "decRef: interiorPtr")
|
||||
gcAssert(c.refcount >=% rcIncrement, "decRef")
|
||||
if --c.refcount:
|
||||
rtlAddZCT(c)
|
||||
|
||||
proc incRef(c: PCell) {.inline.} =
|
||||
gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr")
|
||||
c.refcount = c.refcount +% rcIncrement
|
||||
|
||||
proc nimGCref(p: pointer) {.compilerProc.} =
|
||||
let cell = usrToCell(p)
|
||||
incRef(cell)
|
||||
markAsEscaped(cell)
|
||||
add(gch.additionalRoots, cell)
|
||||
|
||||
proc nimGCunref(p: pointer) {.compilerProc.} =
|
||||
let cell = usrToCell(p)
|
||||
decRef(cell)
|
||||
var L = gch.additionalRoots.len-1
|
||||
var i = L
|
||||
let d = gch.additionalRoots.d
|
||||
@@ -267,6 +237,12 @@ proc nimGCunref(p: pointer) {.compilerProc.} =
|
||||
break
|
||||
dec(i)
|
||||
|
||||
proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} =
|
||||
discard "can we do some freeing here?"
|
||||
|
||||
proc nimGCunrefRC1(p: pointer) {.compilerProc, inline.} =
|
||||
discard "can we do some freeing here?"
|
||||
|
||||
template markGrey(x: PCell) =
|
||||
if x.color != 1-gch.black and gch.phase == Phase.Marking:
|
||||
if not isAllocatedPtr(gch.region, x):
|
||||
@@ -280,59 +256,32 @@ proc GC_addCycleRoot*[T](p: ref T) {.inline.} =
|
||||
## adds 'p' to the cycle candidate set for the cycle collector. It is
|
||||
## necessary if you used the 'acyclic' pragma for optimization
|
||||
## purposes and need to break cycles manually.
|
||||
rtlAddCycleRoot(usrToCell(cast[pointer](p)))
|
||||
discard
|
||||
|
||||
proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} =
|
||||
sysAssert(allocInv(gch.region), "begin nimGCunrefNoCycle")
|
||||
var c = usrToCell(p)
|
||||
gcAssert(isAllocatedPtr(gch.region, c), "nimGCunrefNoCycle: isAllocatedPtr")
|
||||
if --c.refcount:
|
||||
rtlAddZCT(c)
|
||||
sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 2")
|
||||
sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 5")
|
||||
|
||||
proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} =
|
||||
# the code generator calls this proc!
|
||||
template asgnRefImpl =
|
||||
gcAssert(not isOnStack(dest), "asgnRef")
|
||||
# BUGFIX: first incRef then decRef!
|
||||
if src != nil:
|
||||
let s = usrToCell(src)
|
||||
incRef(s)
|
||||
markAsEscaped(s)
|
||||
markGrey(s)
|
||||
if dest[] != nil: decRef(usrToCell(dest[]))
|
||||
dest[] = src
|
||||
|
||||
proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} =
|
||||
# the code generator calls this proc!
|
||||
asgnRefImpl()
|
||||
|
||||
proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} =
|
||||
# the code generator calls this proc if it is known at compile time that no
|
||||
# cycle is possible.
|
||||
gcAssert(not isOnStack(dest), "asgnRefNoCycle")
|
||||
if src != nil:
|
||||
var c = usrToCell(src)
|
||||
++c.refcount
|
||||
markGrey(c)
|
||||
if dest[] != nil:
|
||||
var c = usrToCell(dest[])
|
||||
if --c.refcount:
|
||||
rtlAddZCT(c)
|
||||
dest[] = src
|
||||
asgnRefImpl()
|
||||
|
||||
proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} =
|
||||
# unsureAsgnRef updates the reference counters only if dest is not on the
|
||||
# unsureAsgnRef marks 'src' as grey only if dest is not on the
|
||||
# stack. It is used by the code generator if it cannot decide wether a
|
||||
# reference is in the stack or not (this can happen for var parameters).
|
||||
if not isOnStack(dest):
|
||||
if src != nil:
|
||||
let s = usrToCell(src)
|
||||
incRef(s)
|
||||
markGrey(s)
|
||||
# XXX finally use assembler for the stack checking instead!
|
||||
# the test for '!= nil' is correct, but I got tired of the segfaults
|
||||
# resulting from the crappy stack checking:
|
||||
if cast[int](dest[]) >=% PageSize: decRef(usrToCell(dest[]))
|
||||
else:
|
||||
# can't be an interior pointer if it's a stack location!
|
||||
gcAssert(interiorAllocatedPtr(gch.region, dest) == nil,
|
||||
"stack loc AND interior pointer")
|
||||
if src != nil:
|
||||
let s = usrToCell(src)
|
||||
markAsEscaped(s)
|
||||
if not isOnStack(dest): markGrey(s)
|
||||
dest[] = src
|
||||
|
||||
type
|
||||
@@ -366,7 +315,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
|
||||
if dest == nil: return # nothing to do
|
||||
if ntfNoRefs notin mt.flags:
|
||||
case mt.kind
|
||||
of tyRef, tyString, tySequence: # leaf:
|
||||
of tyRef, tyOptAsRef, tyString, tySequence: # leaf:
|
||||
doOperation(cast[PPointer](d)[], op)
|
||||
of tyObject, tyTuple:
|
||||
forAllSlotsAux(dest, mt.node, op)
|
||||
@@ -379,13 +328,13 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
|
||||
gcAssert(cell != nil, "forAllChildren: 1")
|
||||
gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2")
|
||||
gcAssert(cell.typ != nil, "forAllChildren: 3")
|
||||
gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 4"
|
||||
gcAssert cell.typ.kind in {tyRef, tyOptAsRef, tySequence, tyString}, "forAllChildren: 4"
|
||||
let marker = cell.typ.marker
|
||||
if marker != nil:
|
||||
marker(cellToUsr(cell), op.int)
|
||||
else:
|
||||
case cell.typ.kind
|
||||
of tyRef: # common case
|
||||
of tyRef, tyOptAsRef: # common case
|
||||
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
|
||||
of tySequence:
|
||||
var d = cast[ByteAddress](cellToUsr(cell))
|
||||
@@ -396,50 +345,6 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
|
||||
GenericSeqSize), cell.typ.base, op)
|
||||
else: discard
|
||||
|
||||
proc addNewObjToZCT(res: PCell, gch: var GcHeap) {.inline.} =
|
||||
# we check the last 8 entries (cache line) for a slot that could be reused.
|
||||
# In 63% of all cases we succeed here! But we have to optimize the heck
|
||||
# out of this small linear search so that ``newObj`` is not slowed down.
|
||||
#
|
||||
# Slots to try cache hit
|
||||
# 1 32%
|
||||
# 4 59%
|
||||
# 8 63%
|
||||
# 16 66%
|
||||
# all slots 68%
|
||||
var L = gch.zct.len
|
||||
var d = gch.zct.d
|
||||
when true:
|
||||
# loop unrolled for performance:
|
||||
template replaceZctEntry(i: expr) =
|
||||
c = d[i]
|
||||
if c.refcount >=% rcIncrement:
|
||||
c.refcount = c.refcount and not ZctFlag
|
||||
d[i] = res
|
||||
return
|
||||
if L > 8:
|
||||
var c: PCell
|
||||
replaceZctEntry(L-1)
|
||||
replaceZctEntry(L-2)
|
||||
replaceZctEntry(L-3)
|
||||
replaceZctEntry(L-4)
|
||||
replaceZctEntry(L-5)
|
||||
replaceZctEntry(L-6)
|
||||
replaceZctEntry(L-7)
|
||||
replaceZctEntry(L-8)
|
||||
add(gch.zct, res)
|
||||
else:
|
||||
d[L] = res
|
||||
inc(gch.zct.len)
|
||||
else:
|
||||
for i in countdown(L-1, max(0, L-8)):
|
||||
var c = d[i]
|
||||
if c.refcount >=% rcIncrement:
|
||||
c.refcount = c.refcount and not ZctFlag
|
||||
d[i] = res
|
||||
return
|
||||
add(gch.zct, res)
|
||||
|
||||
{.push stackTrace: off, profiler:off.}
|
||||
proc gcInvariant*() =
|
||||
sysAssert(allocInv(gch.region), "injected")
|
||||
@@ -447,10 +352,12 @@ proc gcInvariant*() =
|
||||
markForDebug(gch)
|
||||
{.pop.}
|
||||
|
||||
include gc_common
|
||||
|
||||
proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
|
||||
# generates a new object and sets its reference counter to 0
|
||||
sysAssert(allocInv(gch.region), "rawNewObj begin")
|
||||
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
|
||||
gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
|
||||
collectCT(gch)
|
||||
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
|
||||
gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
|
||||
@@ -461,10 +368,8 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
|
||||
res.filename = framePtr.prev.filename
|
||||
res.line = framePtr.prev.line
|
||||
# refcount is zero, color is black, but mark it to be in the ZCT
|
||||
res.refcount = ZctFlag or allocColor()
|
||||
res.refcount = allocColor()
|
||||
sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
|
||||
# its refcount is zero, so add it to the ZCT:
|
||||
addNewObjToZCT(res, gch)
|
||||
when logGC: writeCell("new cell", res)
|
||||
gcTrace(res, csAllocated)
|
||||
when useCellIds:
|
||||
@@ -493,95 +398,38 @@ proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
# generates a new object and sets its reference counter to 1
|
||||
sysAssert(allocInv(gch.region), "newObjRC1 begin")
|
||||
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
|
||||
collectCT(gch)
|
||||
sysAssert(allocInv(gch.region), "newObjRC1 after collectCT")
|
||||
|
||||
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
|
||||
sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc")
|
||||
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
|
||||
# now it is buffered in the ZCT
|
||||
res.typ = typ
|
||||
when leakDetector:
|
||||
if framePtr != nil and framePtr.prev != nil:
|
||||
res.filename = framePtr.prev.filename
|
||||
res.line = framePtr.prev.line
|
||||
res.refcount = rcIncrement or allocColor() # refcount is 1
|
||||
sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
|
||||
when logGC: writeCell("new cell", res)
|
||||
gcTrace(res, csAllocated)
|
||||
when useCellIds:
|
||||
inc gch.idGenerator
|
||||
res.id = gch.idGenerator
|
||||
result = cellToUsr(res)
|
||||
zeroMem(result, size)
|
||||
sysAssert(allocInv(gch.region), "newObjRC1 end")
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
result = newObj(typ, size)
|
||||
|
||||
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
|
||||
result = newObjRC1(typ, size)
|
||||
cast[PGenericSeq](result).len = len
|
||||
cast[PGenericSeq](result).reserved = len
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
result = newSeq(typ, len)
|
||||
|
||||
proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
|
||||
acquire(gch)
|
||||
collectCT(gch)
|
||||
var ol = usrToCell(old)
|
||||
gcAssert(isAllocatedPtr(gch.region, ol), "growObj: freed pointer?")
|
||||
|
||||
sysAssert(ol.typ != nil, "growObj: 1")
|
||||
gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
|
||||
sysAssert(allocInv(gch.region), "growObj begin")
|
||||
|
||||
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
|
||||
var elemSize = 1
|
||||
if ol.typ.kind != tyString: elemSize = ol.typ.base.size
|
||||
incTypeSize ol.typ, newsize
|
||||
|
||||
let oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
|
||||
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
|
||||
copyMem(res, ol, oldsize + sizeof(Cell))
|
||||
zeroMem(cast[pointer](cast[ByteAddress](res) +% oldsize +% sizeof(Cell)),
|
||||
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
|
||||
newsize-oldsize)
|
||||
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
|
||||
# This can be wrong for intermediate temps that are nevertheless on the
|
||||
# heap because of lambda lifting:
|
||||
#gcAssert(res.refcount shr rcShift <=% 1, "growObj: 4")
|
||||
when logGC:
|
||||
writeCell("growObj old cell", ol)
|
||||
writeCell("growObj new cell", res)
|
||||
gcTrace(ol, csZctFreed)
|
||||
gcTrace(res, csAllocated)
|
||||
when reallyDealloc:
|
||||
sysAssert(allocInv(gch.region), "growObj before dealloc")
|
||||
if ol.refcount shr rcShift <=% 1:
|
||||
# free immediately to save space:
|
||||
if (ol.refcount and ZctFlag) != 0:
|
||||
var j = gch.zct.len-1
|
||||
var d = gch.zct.d
|
||||
while j >= 0:
|
||||
if d[j] == ol:
|
||||
d[j] = res
|
||||
break
|
||||
dec(j)
|
||||
rawDealloc(gch.region, ol)
|
||||
when false:
|
||||
# this is wrong since seqs can be shared via 'shallow':
|
||||
when reallyDealloc: rawDealloc(gch.region, ol)
|
||||
else:
|
||||
# we split the old refcount in 2 parts. XXX This is still not entirely
|
||||
# correct if the pointer that receives growObj's result is on the stack.
|
||||
# A better fix would be to emit the location specific write barrier for
|
||||
# 'growObj', but this is lots of more work and who knows what new problems
|
||||
# this would create.
|
||||
res.refcount = rcIncrement or allocColor()
|
||||
decRef(ol)
|
||||
else:
|
||||
sysAssert(ol.typ != nil, "growObj: 5")
|
||||
zeroMem(ol, sizeof(Cell))
|
||||
zeroMem(ol, sizeof(Cell))
|
||||
when useCellIds:
|
||||
inc gch.idGenerator
|
||||
res.id = gch.idGenerator
|
||||
release(gch)
|
||||
result = cellToUsr(res)
|
||||
sysAssert(allocInv(gch.region), "growObj end")
|
||||
when defined(memProfiler): nimProfile(newsize-oldsize)
|
||||
|
||||
proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
|
||||
@@ -637,12 +485,13 @@ proc GC_dumpHeap*(file: File) =
|
||||
## can be translated into "dot" syntax via the "heapdump2dot" tool.
|
||||
gch.pDumpHeapFile = file
|
||||
var spaceIter: ObjectSpaceIter
|
||||
var d = gch.decStack.d
|
||||
for i in 0 .. < gch.decStack.len:
|
||||
if isAllocatedPtr(gch.region, d[i]):
|
||||
c_fprintf(file, "onstack %p\n", d[i])
|
||||
else:
|
||||
c_fprintf(file, "onstack_invalid %p\n", d[i])
|
||||
when false:
|
||||
var d = gch.decStack.d
|
||||
for i in 0 .. < gch.decStack.len:
|
||||
if isAllocatedPtr(gch.region, d[i]):
|
||||
c_fprintf(file, "onstack %p\n", d[i])
|
||||
else:
|
||||
c_fprintf(file, "onstack_invalid %p\n", d[i])
|
||||
for i in 0 .. < globalMarkersLen: globalMarkers[i]()
|
||||
while true:
|
||||
let x = allObjectsAsProc(gch.region, addr spaceIter)
|
||||
@@ -667,14 +516,6 @@ proc GC_dumpHeap() =
|
||||
|
||||
proc freeCyclicCell(gch: var GcHeap, c: PCell) =
|
||||
gcAssert(isAllocatedPtr(gch.region, c), "freeCyclicCell: freed pointer?")
|
||||
|
||||
var d = gch.decStack.d
|
||||
for i in 0..gch.decStack.len-1:
|
||||
if d[i] == c:
|
||||
writeCell("freeing ", c)
|
||||
GC_dumpHeap()
|
||||
gcAssert d[i] != c, "wtf man, freeing obviously alive stuff?!!"
|
||||
|
||||
prepareDealloc(c)
|
||||
gcTrace(c, csCycFreed)
|
||||
when logGC: writeCell("cycle collector dealloc cell", c)
|
||||
@@ -713,15 +554,6 @@ proc markRoot(gch: var GcHeap, c: PCell) {.inline.} =
|
||||
if c.color == 1-gch.black:
|
||||
c.setColor(rcGrey)
|
||||
add(gch.greyStack, c)
|
||||
elif c.color == rcGrey:
|
||||
var isGrey = false
|
||||
var d = gch.decStack.d
|
||||
for i in 0..gch.decStack.len-1:
|
||||
if d[i] == c:
|
||||
isGrey = true
|
||||
break
|
||||
if not isGrey:
|
||||
gcAssert false, "markRoot: root is already grey?!"
|
||||
|
||||
proc markIncremental(gch: var GcHeap): bool =
|
||||
var L = addr(gch.greyStack.len)
|
||||
@@ -741,30 +573,14 @@ proc markIncremental(gch: var GcHeap): bool =
|
||||
c.setColor(gch.black)
|
||||
forAllChildren(c, waMarkGrey)
|
||||
elif c.color == (1-gch.black):
|
||||
gcAssert false, "wtf why are there white object in the greystack?"
|
||||
gcAssert false, "wtf why are there white objects in the greystack?"
|
||||
checkTime()
|
||||
gcAssert gch.greyStack.len == 0, "markIncremental: greystack not empty "
|
||||
|
||||
# assert that all local roots are black by now:
|
||||
var d = gch.decStack.d
|
||||
var errors = false
|
||||
for i in 0..gch.decStack.len-1:
|
||||
gcAssert(isAllocatedPtr(gch.region, d[i]), "markIncremental: isAllocatedPtr 2")
|
||||
if d[i].color != gch.black:
|
||||
writeCell("not black ", d[i])
|
||||
errors = true
|
||||
gcAssert(not errors, "wtf something wrong hre")
|
||||
result = true
|
||||
|
||||
proc markGlobals(gch: var GcHeap) =
|
||||
for i in 0 .. < globalMarkersLen: globalMarkers[i]()
|
||||
|
||||
proc markLocals(gch: var GcHeap) =
|
||||
var d = gch.decStack.d
|
||||
for i in 0 .. < gch.decStack.len:
|
||||
sysAssert isAllocatedPtr(gch.region, d[i]), "markLocals"
|
||||
markRoot(gch, d[i])
|
||||
|
||||
proc doOperation(p: pointer, op: WalkOp) =
|
||||
if p == nil: return
|
||||
var c: PCell = usrToCell(p)
|
||||
@@ -776,11 +592,7 @@ proc doOperation(p: pointer, op: WalkOp) =
|
||||
#if not isAllocatedPtr(gch.region, c):
|
||||
# c_fprintf(stdout, "[GC] decref bug: %p", c)
|
||||
gcAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef")
|
||||
gcAssert(c.refcount >=% rcIncrement, "doOperation 2")
|
||||
#c.refcount = c.refcount -% rcIncrement
|
||||
when logGC: writeCell("decref (from doOperation)", c)
|
||||
decRef(c)
|
||||
#if c.refcount <% rcIncrement: addZCT(gch.zct, c)
|
||||
discard "use me for nimEscape?"
|
||||
of waMarkGlobal:
|
||||
template handleRoot =
|
||||
if gch.dumpHeapFile.isNil:
|
||||
@@ -811,40 +623,6 @@ proc doOperation(p: pointer, op: WalkOp) =
|
||||
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
|
||||
doOperation(d, WalkOp(op))
|
||||
|
||||
proc collectZCT(gch: var GcHeap): bool {.benign.}
|
||||
|
||||
proc collectCycles(gch: var GcHeap): bool =
|
||||
when hasThreadSupport:
|
||||
for c in gch.toDispose:
|
||||
nimGCunref(c)
|
||||
|
||||
# ensure the ZCT 'color' is not used:
|
||||
while gch.zct.len > 0: discard collectZCT(gch)
|
||||
|
||||
case gch.phase
|
||||
of Phase.None:
|
||||
gch.phase = Phase.Marking
|
||||
markGlobals(gch)
|
||||
|
||||
c_fprintf(stdout, "collectCycles: introduced bug E %ld\n", gch.phase)
|
||||
discard allocInv(gch.region)
|
||||
of Phase.Marking:
|
||||
# since locals do not have a write barrier, we need
|
||||
# to keep re-scanning them :-( but there is really nothing we can
|
||||
# do about that.
|
||||
markLocals(gch)
|
||||
if markIncremental(gch):
|
||||
gch.phase = Phase.Sweeping
|
||||
gch.red = 1 - gch.red
|
||||
of Phase.Sweeping:
|
||||
gcAssert gch.greyStack.len == 0, "greystack not empty"
|
||||
if sweep(gch):
|
||||
gch.phase = Phase.None
|
||||
# flip black/white meanings:
|
||||
gch.black = 1 - gch.black
|
||||
gcAssert gch.red == 1 - gch.black, "red color is wrong"
|
||||
result = true
|
||||
|
||||
proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
|
||||
# the addresses are not as cells on the stack, so turn them to cells:
|
||||
sysAssert(allocInv(gch.region), "gcMark begin")
|
||||
@@ -855,62 +633,43 @@ proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
|
||||
var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell))
|
||||
if objStart != nil:
|
||||
# mark the cell:
|
||||
objStart.refcount = objStart.refcount +% rcIncrement
|
||||
add(gch.decStack, objStart)
|
||||
markRoot(gch, objStart)
|
||||
sysAssert(allocInv(gch.region), "gcMark end")
|
||||
|
||||
include gc_common
|
||||
|
||||
proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
|
||||
forEachStackSlot(gch, gcMark)
|
||||
|
||||
proc collectZCT(gch: var GcHeap): bool =
|
||||
# Note: Freeing may add child objects to the ZCT! So essentially we do
|
||||
# deep freeing, which is bad for incremental operation. In order to
|
||||
# avoid a deep stack, we move objects to keep the ZCT small.
|
||||
# This is performance critical!
|
||||
var L = addr(gch.zct.len)
|
||||
takeStartTime(100)
|
||||
|
||||
while L[] > 0:
|
||||
var c = gch.zct.d[0]
|
||||
sysAssert(isAllocatedPtr(gch.region, c), "CollectZCT: isAllocatedPtr")
|
||||
# remove from ZCT:
|
||||
gcAssert((c.refcount and ZctFlag) == ZctFlag, "collectZCT")
|
||||
|
||||
c.refcount = c.refcount and not ZctFlag
|
||||
gch.zct.d[0] = gch.zct.d[L[] - 1]
|
||||
dec(L[])
|
||||
takeTime()
|
||||
if c.refcount <% rcIncrement and c.color != rcGrey:
|
||||
# It may have a RC > 0, if it is in the hardware stack or
|
||||
# it has not been removed yet from the ZCT. This is because
|
||||
# ``incref`` does not bother to remove the cell from the ZCT
|
||||
# as this might be too slow.
|
||||
# In any case, it should be removed from the ZCT. But not
|
||||
# freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!**
|
||||
when logGC: writeCell("zct dealloc cell", c)
|
||||
gcTrace(c, csZctFreed)
|
||||
# We are about to free the object, call the finalizer BEFORE its
|
||||
# children are deleted as well, because otherwise the finalizer may
|
||||
# access invalid memory. This is done by prepareDealloc():
|
||||
prepareDealloc(c)
|
||||
forAllChildren(c, waZctDecRef)
|
||||
when reallyDealloc:
|
||||
sysAssert(allocInv(gch.region), "collectZCT: rawDealloc")
|
||||
rawDealloc(gch.region, c)
|
||||
else:
|
||||
sysAssert(c.typ != nil, "collectZCT 2")
|
||||
zeroMem(c, sizeof(Cell))
|
||||
checkTime()
|
||||
result = true
|
||||
|
||||
proc unmarkStackAndRegisters(gch: var GcHeap) =
|
||||
var d = gch.decStack.d
|
||||
for i in 0..gch.decStack.len-1:
|
||||
sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters"
|
||||
decRef(d[i])
|
||||
gch.decStack.len = 0
|
||||
proc collectALittle(gch: var GcHeap): bool =
|
||||
case gch.phase
|
||||
of Phase.None:
|
||||
if getOccupiedMem(gch.region) >= gch.cycleThreshold:
|
||||
gch.phase = Phase.Marking
|
||||
markGlobals(gch)
|
||||
result = collectALittle(gch)
|
||||
#when false: c_fprintf(stdout, "collectALittle: introduced bug E %ld\n", gch.phase)
|
||||
#discard allocInv(gch.region)
|
||||
of Phase.Marking:
|
||||
when hasThreadSupport:
|
||||
for c in gch.toDispose:
|
||||
nimGCunref(c)
|
||||
prepareForInteriorPointerChecking(gch.region)
|
||||
markStackAndRegisters(gch)
|
||||
inc(gch.stat.stackScans)
|
||||
if markIncremental(gch):
|
||||
gch.phase = Phase.Sweeping
|
||||
gch.red = 1 - gch.red
|
||||
of Phase.Sweeping:
|
||||
gcAssert gch.greyStack.len == 0, "greystack not empty"
|
||||
when hasThreadSupport:
|
||||
for c in gch.toDispose:
|
||||
nimGCunref(c)
|
||||
if sweep(gch):
|
||||
gch.phase = Phase.None
|
||||
# flip black/white meanings:
|
||||
gch.black = 1 - gch.black
|
||||
gcAssert gch.red == 1 - gch.black, "red color is wrong"
|
||||
inc(gch.stat.completedCollections)
|
||||
result = true
|
||||
|
||||
proc collectCTBody(gch: var GcHeap) =
|
||||
when withRealTime:
|
||||
@@ -919,22 +678,12 @@ proc collectCTBody(gch: var GcHeap) =
|
||||
|
||||
when not nimCoroutines:
|
||||
gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
|
||||
sysAssert(gch.decStack.len == 0, "collectCT")
|
||||
prepareForInteriorPointerChecking(gch.region)
|
||||
markStackAndRegisters(gch)
|
||||
gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len)
|
||||
inc(gch.stat.stackScans)
|
||||
if collectZCT(gch):
|
||||
when cycleGC:
|
||||
if getOccupiedMem(gch.region) >= gch.cycleThreshold or alwaysCycleGC:
|
||||
if collectCycles(gch):
|
||||
inc(gch.stat.cycleCollections)
|
||||
gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
|
||||
CycleIncrease)
|
||||
gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
|
||||
unmarkStackAndRegisters(gch)
|
||||
#gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len)
|
||||
if collectALittle(gch):
|
||||
gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
|
||||
CycleIncrease)
|
||||
gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
|
||||
sysAssert(allocInv(gch.region), "collectCT: end")
|
||||
|
||||
when withRealTime:
|
||||
let duration = getticks() - t0
|
||||
gch.stat.maxPause = max(gch.stat.maxPause, duration)
|
||||
@@ -955,7 +704,7 @@ proc collectCT(gch: var GcHeap) =
|
||||
let stackMarkCosts = max(currentStackSizes() div (16*sizeof(int)), ZctThreshold)
|
||||
else:
|
||||
let stackMarkCosts = max(stackSize() div (16*sizeof(int)), ZctThreshold)
|
||||
if (gch.zct.len >= stackMarkCosts or (cycleGC and
|
||||
if (gch.greyStack.len >= stackMarkCosts or (cycleGC and
|
||||
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
|
||||
gch.recGcLock == 0:
|
||||
collectCTBody(gch)
|
||||
@@ -969,10 +718,9 @@ when withRealTime:
|
||||
|
||||
proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) =
|
||||
gch.maxPause = us.toNano
|
||||
if (gch.zct.len >= ZctThreshold or (cycleGC and
|
||||
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or
|
||||
strongAdvice:
|
||||
collectCTBody(gch)
|
||||
#if (getOccupiedMem(gch.region)>=gch.cycleThreshold) or
|
||||
# alwaysGC or strongAdvice:
|
||||
collectCTBody(gch)
|
||||
|
||||
proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} =
|
||||
if stackSize >= 0:
|
||||
@@ -1010,12 +758,8 @@ when not defined(useNimRtl):
|
||||
proc GC_setStrategy(strategy: GC_Strategy) =
|
||||
discard
|
||||
|
||||
proc GC_enableMarkAndSweep() =
|
||||
gch.cycleThreshold = InitialCycleThreshold
|
||||
|
||||
proc GC_disableMarkAndSweep() =
|
||||
gch.cycleThreshold = high(gch.cycleThreshold)-1
|
||||
# set to the max value to suppress the cycle detector
|
||||
proc GC_enableMarkAndSweep() = discard
|
||||
proc GC_disableMarkAndSweep() = discard
|
||||
|
||||
proc GC_fullCollect() =
|
||||
var oldThreshold = gch.cycleThreshold
|
||||
@@ -1029,17 +773,17 @@ when not defined(useNimRtl):
|
||||
"[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
|
||||
"[GC] stack scans: " & $gch.stat.stackScans & "\n" &
|
||||
"[GC] stack cells: " & $gch.stat.maxStackCells & "\n" &
|
||||
"[GC] cycle collections: " & $gch.stat.cycleCollections & "\n" &
|
||||
"[GC] completed collections: " & $gch.stat.completedCollections & "\n" &
|
||||
"[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
|
||||
"[GC] zct capacity: " & $gch.zct.cap & "\n" &
|
||||
"[GC] grey stack capacity: " & $gch.greyStack.cap & "\n" &
|
||||
"[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
|
||||
"[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000)
|
||||
"[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000) & "\n"
|
||||
when nimCoroutines:
|
||||
result = result & "[GC] number of stacks: " & $gch.stack.len & "\n"
|
||||
result.add "[GC] number of stacks: " & $gch.stack.len & "\n"
|
||||
for stack in items(gch.stack):
|
||||
result = result & "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & $stack.maxStackSize & "\n"
|
||||
result.add "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & $stack.maxStackSize & "\n"
|
||||
else:
|
||||
result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
|
||||
result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
|
||||
GC_enable()
|
||||
|
||||
{.pop.}
|
||||
|
||||
@@ -373,12 +373,22 @@ proc deallocHeap*(runFinalizers = true; allowGcAfterwards = true) =
|
||||
## is true. If ``allowGcAfterwards`` is true, a minimal amount of allocation
|
||||
## happens to ensure the GC can continue to work after the call
|
||||
## to ``deallocHeap``.
|
||||
template deallocCell(x) =
|
||||
if isCell(x):
|
||||
# cast to PCell is correct here:
|
||||
prepareDealloc(cast[PCell](x))
|
||||
|
||||
if runFinalizers:
|
||||
for x in allObjects(gch.region):
|
||||
if isCell(x):
|
||||
# cast to PCell is correct here:
|
||||
var c = cast[PCell](x)
|
||||
prepareDealloc(c)
|
||||
when not declared(allObjectsAsProc):
|
||||
for x in allObjects(gch.region):
|
||||
deallocCell(x)
|
||||
else:
|
||||
var spaceIter: ObjectSpaceIter
|
||||
while true:
|
||||
let x = allObjectsAsProc(gch.region, addr spaceIter)
|
||||
if spaceIter.state < 0: break
|
||||
deallocCell(x)
|
||||
|
||||
deallocOsPages(gch.region)
|
||||
zeroMem(addr gch.region, sizeof(gch.region))
|
||||
if allowGcAfterwards:
|
||||
|
||||
@@ -252,7 +252,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
|
||||
if dest == nil: return # nothing to do
|
||||
if ntfNoRefs notin mt.flags:
|
||||
case mt.kind
|
||||
of tyRef, tyString, tySequence: # leaf:
|
||||
of tyRef, tyOptAsRef, tyString, tySequence: # leaf:
|
||||
doOperation(cast[PPointer](d)[], op)
|
||||
of tyObject, tyTuple:
|
||||
forAllSlotsAux(dest, mt.node, op)
|
||||
@@ -264,13 +264,13 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
|
||||
proc forAllChildren(cell: PCell, op: WalkOp) =
|
||||
gcAssert(cell != nil, "forAllChildren: 1")
|
||||
gcAssert(cell.typ != nil, "forAllChildren: 2")
|
||||
gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 3"
|
||||
gcAssert cell.typ.kind in {tyRef, tyOptAsRef, tySequence, tyString}, "forAllChildren: 3"
|
||||
let marker = cell.typ.marker
|
||||
if marker != nil:
|
||||
marker(cellToUsr(cell), op.int)
|
||||
else:
|
||||
case cell.typ.kind
|
||||
of tyRef: # common case
|
||||
of tyRef, tyOptAsRef: # common case
|
||||
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
|
||||
of tySequence:
|
||||
var d = cast[ByteAddress](cellToUsr(cell))
|
||||
@@ -285,7 +285,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
|
||||
# generates a new object and sets its reference counter to 0
|
||||
incTypeSize typ, size
|
||||
acquire(gch)
|
||||
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
|
||||
gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
|
||||
collectCT(gch)
|
||||
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
|
||||
gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
|
||||
@@ -526,10 +526,10 @@ when not defined(useNimRtl):
|
||||
"[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
|
||||
"[GC] freed objects: " & $gch.stat.freedObjects & "\n"
|
||||
when nimCoroutines:
|
||||
result = result & "[GC] number of stacks: " & $gch.stack.len & "\n"
|
||||
result.add "[GC] number of stacks: " & $gch.stack.len & "\n"
|
||||
for stack in items(gch.stack):
|
||||
result = result & "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & $stack.maxStackSize & "\n"
|
||||
result.add "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & $stack.maxStackSize & "\n"
|
||||
else:
|
||||
result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
|
||||
result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
|
||||
|
||||
{.pop.}
|
||||
|
||||
@@ -62,6 +62,21 @@ type
|
||||
tyUInt16,
|
||||
tyUInt32,
|
||||
tyUInt64,
|
||||
tyOptAsRef, tyUnused1, tyUnused2,
|
||||
tyVarargsHidden,
|
||||
tyUnusedHidden,
|
||||
tyProxyHidden,
|
||||
tyBuiltInTypeClassHidden,
|
||||
tyUserTypeClassHidden,
|
||||
tyUserTypeClassInstHidden,
|
||||
tyCompositeTypeClassHidden,
|
||||
tyInferredHidden,
|
||||
tyAndHidden, tyOrHidden, tyNotHidden,
|
||||
tyAnythingHidden,
|
||||
tyStaticHidden,
|
||||
tyFromExprHidden,
|
||||
tyOpt,
|
||||
tyVoidHidden
|
||||
|
||||
TNimNodeKind = enum nkNone, nkSlot, nkList, nkCase
|
||||
TNimNode {.codegenType.} = object
|
||||
|
||||
@@ -564,7 +564,11 @@ else:
|
||||
|
||||
when not declared(nimNewSeqOfCap):
|
||||
proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
|
||||
result = newObj(typ, addInt(mulInt(cap, typ.base.size), GenericSeqSize))
|
||||
let s = addInt(mulInt(cap, typ.base.size), GenericSeqSize)
|
||||
when declared(newObjNoInit):
|
||||
result = if ntfNoRefs in typ.base.flags: newObjNoInit(typ, s) else: newObj(typ, s)
|
||||
else:
|
||||
result = newObj(typ, s)
|
||||
cast[PGenericSeq](result).len = 0
|
||||
cast[PGenericSeq](result).reserved = cap
|
||||
|
||||
|
||||
@@ -11,6 +11,15 @@
|
||||
# Nim's configuration system now uses Nim for scripting. This module provides
|
||||
# a few things that are required for this to work.
|
||||
|
||||
const
|
||||
buildOS* {.magic: "BuildOS".}: string = ""
|
||||
## The OS this build is running on. Can be different from ``system.hostOS``
|
||||
## for cross compilations.
|
||||
|
||||
buildCPU* {.magic: "BuildCPU".}: string = ""
|
||||
## The CPU this build is running on. Can be different from ``system.hostCPU``
|
||||
## for cross compilations.
|
||||
|
||||
template builtin = discard
|
||||
|
||||
# We know the effects better than the compiler:
|
||||
|
||||
@@ -95,6 +95,9 @@ proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} =
|
||||
if str == nil: NimString(nil)
|
||||
else: toNimStr(str, str.len)
|
||||
|
||||
template wasMoved(x: NimString): bool = false
|
||||
# (x.reserved and seqShallowFlag) != 0
|
||||
|
||||
proc copyString(src: NimString): NimString {.compilerRtl.} =
|
||||
if src != nil:
|
||||
if (src.reserved and seqShallowFlag) != 0:
|
||||
@@ -103,6 +106,16 @@ proc copyString(src: NimString): NimString {.compilerRtl.} =
|
||||
result = rawNewStringNoInit(src.len)
|
||||
result.len = src.len
|
||||
copyMem(addr(result.data), addr(src.data), src.len + 1)
|
||||
sysAssert((seqShallowFlag and result.reserved) == 0, "copyString")
|
||||
when defined(nimShallowStrings):
|
||||
if (src.reserved and strlitFlag) != 0:
|
||||
result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag
|
||||
|
||||
proc newOwnedString(src: NimString; n: int): NimString =
|
||||
result = rawNewStringNoInit(n)
|
||||
result.len = n
|
||||
copyMem(addr(result.data), addr(src.data), n)
|
||||
result.data[n] = '\0'
|
||||
|
||||
proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
|
||||
if src != nil:
|
||||
@@ -116,6 +129,10 @@ proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
|
||||
result = rawNewStringNoInit(src.len)
|
||||
result.len = src.len
|
||||
copyMem(addr(result.data), addr(src.data), src.len + 1)
|
||||
sysAssert((seqShallowFlag and result.reserved) == 0, "copyStringRC1")
|
||||
when defined(nimShallowStrings):
|
||||
if (src.reserved and strlitFlag) != 0:
|
||||
result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag
|
||||
|
||||
proc copyDeepString(src: NimString): NimString {.inline.} =
|
||||
if src != nil:
|
||||
@@ -140,9 +157,12 @@ proc addChar(s: NimString, c: char): NimString =
|
||||
# is compilerproc!
|
||||
result = s
|
||||
if result.len >= result.space:
|
||||
result.reserved = resize(result.space)
|
||||
let r = resize(result.space)
|
||||
result = cast[NimString](growObj(result,
|
||||
sizeof(TGenericSeq) + result.reserved + 1))
|
||||
sizeof(TGenericSeq) + r + 1))
|
||||
result.reserved = r
|
||||
elif wasMoved(s):
|
||||
result = newOwnedString(s, s.len)
|
||||
result.data[result.len] = c
|
||||
result.data[result.len+1] = '\0'
|
||||
inc(result.len)
|
||||
@@ -179,7 +199,7 @@ proc addChar(s: NimString, c: char): NimString =
|
||||
# s = rawNewString(0);
|
||||
|
||||
proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
|
||||
if dest.len + addlen <= dest.space:
|
||||
if dest.len + addlen <= dest.space and not wasMoved(dest):
|
||||
result = dest
|
||||
else: # slow path:
|
||||
var sp = max(resize(dest.space), dest.len + addlen)
|
||||
@@ -200,7 +220,9 @@ proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} =
|
||||
|
||||
proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
|
||||
var n = max(newLen, 0)
|
||||
if n <= s.space:
|
||||
if wasMoved(s):
|
||||
result = newOwnedString(s, n)
|
||||
elif n <= s.space:
|
||||
result = s
|
||||
else:
|
||||
result = resizeString(s, n)
|
||||
@@ -218,26 +240,29 @@ proc incrSeq(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
|
||||
# seq[seq->len-1] = x;
|
||||
result = seq
|
||||
if result.len >= result.space:
|
||||
result.reserved = resize(result.space)
|
||||
result = cast[PGenericSeq](growObj(result, elemSize * result.reserved +
|
||||
let r = resize(result.space)
|
||||
result = cast[PGenericSeq](growObj(result, elemSize * r +
|
||||
GenericSeqSize))
|
||||
result.reserved = r
|
||||
inc(result.len)
|
||||
|
||||
proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
|
||||
# incrSeq version 2
|
||||
result = seq
|
||||
if result.len >= result.space:
|
||||
result.reserved = resize(result.space)
|
||||
result = cast[PGenericSeq](growObj(result, elemSize * result.reserved +
|
||||
let r = resize(result.space)
|
||||
result = cast[PGenericSeq](growObj(result, elemSize * r +
|
||||
GenericSeqSize))
|
||||
result.reserved = r
|
||||
|
||||
proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
|
||||
compilerRtl.} =
|
||||
result = seq
|
||||
if result.space < newLen:
|
||||
result.reserved = max(resize(result.space), newLen)
|
||||
result = cast[PGenericSeq](growObj(result, elemSize * result.reserved +
|
||||
let r = max(resize(result.space), newLen)
|
||||
result = cast[PGenericSeq](growObj(result, elemSize * r +
|
||||
GenericSeqSize))
|
||||
result.reserved = r
|
||||
elif newLen < result.len:
|
||||
# we need to decref here, otherwise the GC leaks!
|
||||
when not defined(boehmGC) and not defined(nogc) and
|
||||
|
||||
@@ -127,7 +127,8 @@ elif defined(genode):
|
||||
proc initThread(s: var SysThread,
|
||||
stackSize: culonglong,
|
||||
entry: GenodeThreadProc,
|
||||
arg: pointer) {.
|
||||
arg: pointer,
|
||||
affinity: cuint) {.
|
||||
importcpp: "#.initThread(genodeEnv, @)".}
|
||||
|
||||
proc threadVarAlloc(): ThreadVarSlot = 0
|
||||
@@ -567,6 +568,9 @@ when hostOS == "windows":
|
||||
setThreadAffinityMask(t.sys, uint(1 shl cpu))
|
||||
|
||||
elif defined(genode):
|
||||
var affinityOffset: cuint = 1
|
||||
# CPU affinity offset for next thread, safe to roll-over
|
||||
|
||||
proc createThread*[TArg](t: var Thread[TArg],
|
||||
tp: proc (arg: TArg) {.thread, nimcall.},
|
||||
param: TArg) =
|
||||
@@ -577,7 +581,8 @@ elif defined(genode):
|
||||
when hasSharedHeap: t.stackSize = ThreadStackSize
|
||||
t.sys.initThread(
|
||||
ThreadStackSize.culonglong,
|
||||
threadProcWrapper[TArg], addr(t))
|
||||
threadProcWrapper[TArg], addr(t), affinityOffset)
|
||||
inc affinityOffset
|
||||
|
||||
proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) =
|
||||
{.hint: "cannot change Genode thread CPU affinity after initialization".}
|
||||
|
||||
18
tests/array/tarraylen.nim
Normal file
18
tests/array/tarraylen.nim
Normal file
@@ -0,0 +1,18 @@
|
||||
discard """
|
||||
output: ""
|
||||
"""
|
||||
var a: array[0, int]
|
||||
doAssert a.len == 0
|
||||
doAssert array[0..0, int].len == 1
|
||||
doAssert array[0..0, int]([1]).len == 1
|
||||
doAssert array[1..1, int].len == 1
|
||||
doAssert array[1..1, int]([1]).len == 1
|
||||
doAssert array[2, int].len == 2
|
||||
doAssert array[2, int]([1, 2]).len == 2
|
||||
doAssert array[1..3, int].len == 3
|
||||
doAssert array[1..3, int]([1, 2, 3]).len == 3
|
||||
doAssert array[0..2, int].len == 3
|
||||
doAssert array[0..2, int]([1, 2, 3]).len == 3
|
||||
doAssert array[-2 .. -2, int].len == 1
|
||||
doAssert([1, 2, 3].len == 3)
|
||||
doAssert([42].len == 1)
|
||||
@@ -18,7 +18,7 @@ type
|
||||
enumA, enumB, enumC, enumD, enumE, enumLast
|
||||
|
||||
proc vm() =
|
||||
var instructions: array [0..100, MyEnum]
|
||||
var instructions: array[0..100, MyEnum]
|
||||
instructions[2] = enumC
|
||||
instructions[3] = enumD
|
||||
instructions[4] = enumA
|
||||
|
||||
@@ -19,7 +19,7 @@ proc nrow*(dt: DataTable) : Natural =
|
||||
return totalLen
|
||||
|
||||
let
|
||||
stud = Students (id : @[1,2,3], name : @["Vas", "Pas", "NafNaf"], age : @[10,16,32])
|
||||
stud = Students(id : @[1,2,3], name : @["Vas", "Pas", "NafNaf"], age : @[10,16,32])
|
||||
|
||||
echo nrow(stud)
|
||||
|
||||
|
||||
@@ -14,13 +14,13 @@ type
|
||||
|
||||
proc testSem =
|
||||
var
|
||||
things: array [0..1, TComplexRecord] = [
|
||||
things: array[0..1, TComplexRecord] = [
|
||||
(s: "hi", x: 69, y: 45, z: 0.0, chars: {'a', 'b', 'c'}),
|
||||
(s: "hi", x: 69, y: 45, z: 1.0, chars: {'a', 'b', 'c'})]
|
||||
write(stdout, things[0].x)
|
||||
|
||||
const
|
||||
things: array [0..1, TComplexRecord] = [
|
||||
things: array[0..1, TComplexRecord] = [
|
||||
(s: "hi", x: 69, y: 45, z: 0.0, chars: {'a', 'b', 'c'}),
|
||||
(s: "hi", x: 69, y: 45, z: 1.0)] #ERROR
|
||||
otherThings = [ # the same
|
||||
|
||||
@@ -12,7 +12,7 @@ type
|
||||
chars: set[char]]
|
||||
|
||||
const
|
||||
things: array [0..1, TComplexRecord] = [
|
||||
things: array[0..1, TComplexRecord] = [
|
||||
(s: "hi", x: 69, y: 45, z: 0.0, chars: {'a', 'b', 'c'}),
|
||||
(s: "hi", x: 69, y: 45, z: 1.0, chars: {})]
|
||||
otherThings = [ # the same
|
||||
|
||||
@@ -23,29 +23,29 @@ asm """
|
||||
"""
|
||||
|
||||
proc consoleprint (str:cstring): void {.importc: "print", noDecl.}
|
||||
proc print* (a: varargs[string, `$`]) = consoleprint "$1: $2" % [consolePrefix, join (a, " ")]
|
||||
proc print* (a: varargs[string, `$`]) = consoleprint "$1: $2" % [consolePrefix, join(a, " ")]
|
||||
|
||||
type CallbackProc {.importc.} = proc () : cstring
|
||||
|
||||
proc regCallback (fn:CallbackProc) {.importc.}
|
||||
proc runCallbacks ():cstring {.importc.}
|
||||
|
||||
proc `*` (s:string, n:Natural) : string = s.repeat (n)
|
||||
proc `*` (s:string, n:Natural) : string = s.repeat(n)
|
||||
|
||||
proc outer (i:Natural) : (string, int) =
|
||||
let c = $char (random (93) + 33)
|
||||
let n = random (40)
|
||||
let c = $char(random(93) + 33)
|
||||
let n = random(40)
|
||||
let s = c * n
|
||||
proc inner () : cstring = ("[$1]" % $n) & s & " <--"
|
||||
regCallback (inner)
|
||||
proc inner(): cstring = ("[$1]" % $n) & s & " <--"
|
||||
regCallback(inner)
|
||||
return (s, n)
|
||||
|
||||
var expected = "\n"
|
||||
for i in 1 .. 10:
|
||||
let (s, n) = outer (i)
|
||||
let (s, n) = outer(i)
|
||||
expected &= ("($1)[$2]" % [$i, $n]) & s & " <--"
|
||||
expected &= "\n"
|
||||
|
||||
let results = runCallbacks ()
|
||||
let results = runCallbacks()
|
||||
|
||||
doAssert(expected == results)
|
||||
|
||||
@@ -21,7 +21,7 @@ macro debug(n: varargs[untyped]): untyped =
|
||||
add(result, newCall("writeLine", newIdentNode("stdout"), n[i]))
|
||||
|
||||
var
|
||||
a: array [0..10, int]
|
||||
a: array[0..10, int]
|
||||
x = "some string"
|
||||
a[0] = 42
|
||||
a[1] = 45
|
||||
|
||||
@@ -58,10 +58,10 @@ proc create(T: typedesc): T =
|
||||
result.vtbl = getVTable(T)
|
||||
|
||||
proc baseFoo(o: var TBase): int =
|
||||
return cast[fooProc[TBase]](o.vtbl[0]) (o)
|
||||
return cast[fooProc[TBase]](o.vtbl[0])(o)
|
||||
|
||||
proc baseBar(o: var TBase) =
|
||||
cast[barProc[TBase]](o.vtbl[1]) (o)
|
||||
cast[barProc[TBase]](o.vtbl[1])(o)
|
||||
|
||||
var a = TUserObject1.create
|
||||
var b = TUserObject2.create
|
||||
|
||||
@@ -8,7 +8,7 @@ type
|
||||
myEnum = enum e1, e2, e3, e4, e5
|
||||
|
||||
var
|
||||
a: array [myEnum, int]
|
||||
a: array[myEnum, int]
|
||||
|
||||
for i in low(a) .. high(a):
|
||||
a[i] = 0
|
||||
|
||||
21
tests/method/tnildispatcher.nim
Normal file
21
tests/method/tnildispatcher.nim
Normal file
@@ -0,0 +1,21 @@
|
||||
discard """
|
||||
outputsub: '''Error: unhandled exception: cannot dispatch; dispatcher is nil [NilAccessError]'''
|
||||
exitcode: 1
|
||||
"""
|
||||
# bug #5599
|
||||
type
|
||||
Base = ref object of RootObj
|
||||
Derived = ref object of Base
|
||||
|
||||
method inner(obj: Base) {.base.} =
|
||||
quit "to override"
|
||||
|
||||
method outer(obj: Base) {.base.} =
|
||||
echo "outer"
|
||||
obj.inner()
|
||||
|
||||
method inner(obj: Derived) =
|
||||
echo "inner Derived"
|
||||
|
||||
var x: Derived
|
||||
x.outer()
|
||||
@@ -1,6 +1,6 @@
|
||||
# Test a submodule
|
||||
|
||||
#type
|
||||
# TStringArr = array [0.. *] of string
|
||||
# TStringArr = array[0.. *] of string
|
||||
|
||||
proc exportme* = discard
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user