Merge branch 'devel' into araq

This commit is contained in:
Andreas Rumpf
2017-10-02 08:31:38 +02:00
152 changed files with 3012 additions and 3097 deletions

7
changelog.md Normal file
View 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.

View File

@@ -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:

View File

@@ -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):

View File

@@ -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)

View File

@@ -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:

View File

@@ -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

View File

@@ -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))

View File

@@ -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"

View File

@@ -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)

View File

@@ -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 = "\""

View File

@@ -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 &

View File

@@ -107,3 +107,4 @@ proc initDefines*() =
defineSymbol("nimDistros")
defineSymbol("nimHasCppDefine")
defineSymbol("nimGenericInOutFlags")
when false: defineSymbol("nimHasOpt")

View File

@@ -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

View File

@@ -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
View 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))

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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",

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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))

View File

@@ -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".

View File

@@ -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,

View File

@@ -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:

View File

@@ -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):

View File

@@ -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

View File

@@ -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,

View File

@@ -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

View File

@@ -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")

View File

@@ -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 =

View File

@@ -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)

View File

@@ -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:

View File

@@ -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

View File

@@ -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)

View File

@@ -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:

View File

@@ -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)

View File

@@ -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:

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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:

View File

@@ -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))

View File

@@ -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])

View File

@@ -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:

View File

@@ -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

View File

@@ -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:

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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",
":", "::", "=", ".", "..",

View File

@@ -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

View File

@@ -16,6 +16,6 @@ shl shr static
template try tuple type
using
var
when while with without
when while
xor
yield

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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::

View File

@@ -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) =

View File

@@ -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}

View File

@@ -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

View File

@@ -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(); }

View File

@@ -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, `$`]) {.

View File

@@ -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

View File

@@ -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;

View File

@@ -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)):

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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."

View File

@@ -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!")

View File

@@ -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:

View File

@@ -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))

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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:

View File

@@ -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)

View File

@@ -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

View File

@@ -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.}

View File

@@ -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:

View File

@@ -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.}

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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
View 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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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()

View File

@@ -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