Merge branch 'devel' into araq

This commit is contained in:
Andreas Rumpf 2017-10-02 08:31:38 +02:00
commit e9243a1616
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: if a.kind == b.kind:
case a.kind case a.kind
of nkSym: of nkSym:
const varKinds = {skVar, skTemp, skProc} const varKinds = {skVar, skTemp, skProc, skFunc}
# same symbol: aliasing: # same symbol: aliasing:
if a.sym.id == b.sym.id: result = arYes if a.sym.id == b.sym.id: result = arYes
elif a.sym.kind in varKinds or b.sym.kind in varKinds: 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) nkGotoState, # used for the state machine (for iterators)
nkState, # give a label to a code section (for iterators) nkState, # give a label to a code section (for iterators)
nkBreakState, # special break statement for easier code generation nkBreakState, # special break statement for easier code generation
nkFuncDef # a func
TNodeKinds* = set[TNodeKind] TNodeKinds* = set[TNodeKind]
type type
@ -352,7 +354,7 @@ type
tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers
tyFloat, tyFloat32, tyFloat64, tyFloat128, tyFloat, tyFloat32, tyFloat64, tyFloat128,
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64, tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64,
tyUnused0, tyUnused1, tyUnused2, tyOptAsRef, tyUnused1, tyUnused2,
tyVarargs, tyVarargs,
tyUnused, tyUnused,
tyProxy # used as errornous type (for idetools) tyProxy # used as errornous type (for idetools)
@ -402,14 +404,8 @@ type
# instantiation and prior to this it has the potential to # instantiation and prior to this it has the potential to
# be any type. # be any type.
tyFieldAccessor tyOpt
# Expressions such as Type.field (valid in contexts such # Builtin optional type
# 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
tyVoid tyVoid
# now different from tyEmpty, hurray! # now different from tyEmpty, hurray!
@ -534,6 +530,7 @@ type
skConst, # a constant skConst, # a constant
skResult, # special 'result' variable skResult, # special 'result' variable
skProc, # a proc skProc, # a proc
skFunc, # a func
skMethod, # a method skMethod, # a method
skIterator, # an iterator skIterator, # an iterator
skConverter, # a type converter skConverter, # a type converter
@ -552,7 +549,7 @@ type
TSymKinds* = set[TSymKind] TSymKinds* = set[TSymKind]
const const
routineKinds* = {skProc, skMethod, skIterator, routineKinds* = {skProc, skFunc, skMethod, skIterator,
skConverter, skMacro, skTemplate} skConverter, skMacro, skTemplate}
tfIncompleteStruct* = tfVarargs tfIncompleteStruct* = tfVarargs
tfUncheckedArray* = tfVarargs tfUncheckedArray* = tfVarargs
@ -621,7 +618,7 @@ type
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
mNewString, mNewStringOfCap, mParseBiggestFloat, mNewString, mNewStringOfCap, mParseBiggestFloat,
mReset, mReset,
mArray, mOpenArray, mRange, mSet, mSeq, mVarargs, mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs,
mRef, mPtr, mVar, mDistinct, mVoid, mTuple, mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
mOrdinal, mOrdinal,
mInt, mInt8, mInt16, mInt32, mInt64, mInt, mInt8, mInt16, mInt32, mInt64,
@ -631,7 +628,7 @@ type
mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mStmt, mTypeDesc, mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mStmt, mTypeDesc,
mVoidType, mPNimrodNode, mShared, mGuarded, mLock, mSpawn, mDeepCopy, mVoidType, mPNimrodNode, mShared, mGuarded, mLock, mSpawn, mDeepCopy,
mIsMainModule, mCompileDate, mCompileTime, mProcCall, mIsMainModule, mCompileDate, mCompileTime, mProcCall,
mCpuEndian, mHostOS, mHostCPU, mAppType, mCpuEndian, mHostOS, mHostCPU, mBuildOS, mBuildCPU, mAppType,
mNaN, mInf, mNegInf, mNaN, mInf, mNegInf,
mCompileOption, mCompileOptionArg, mCompileOption, mCompileOptionArg,
mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, mNKind, mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, mNKind,
@ -754,9 +751,9 @@ type
TLocFlags* = set[TLocFlag] TLocFlags* = set[TLocFlag]
TLoc* = object TLoc* = object
k*: TLocKind # kind of location k*: TLocKind # kind of location
s*: TStorageLoc storage*: TStorageLoc
flags*: TLocFlags # location's flags 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) r*: Rope # rope value of location (code generators)
dup*: Rope # duplicated location for precise stack scans dup*: Rope # duplicated location for precise stack scans
@ -934,7 +931,7 @@ type
# the poor naming choices in the standard library. # the poor naming choices in the standard library.
const const
OverloadableSyms* = {skProc, skMethod, skIterator, OverloadableSyms* = {skProc, skFunc, skMethod, skIterator,
skConverter, skModule, skTemplate, skMacro} skConverter, skModule, skTemplate, skMacro}
GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody, GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody,
@ -957,7 +954,7 @@ const
tyTuple, tySequence} tyTuple, tySequence}
NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence, NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence,
tyProc, tyString, tyError} tyProc, tyString, tyError}
ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType,
skIterator, skIterator,
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias} skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
@ -981,14 +978,14 @@ const
nkLiterals* = {nkCharLit..nkTripleStrLit} nkLiterals* = {nkCharLit..nkTripleStrLit}
nkLambdaKinds* = {nkLambda, nkDo} nkLambdaKinds* = {nkLambda, nkDo}
declarativeDefs* = {nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef} declarativeDefs* = {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef}
procDefs* = nkLambdaKinds + declarativeDefs procDefs* = nkLambdaKinds + declarativeDefs
nkSymChoices* = {nkClosedSymChoice, nkOpenSymChoice} nkSymChoices* = {nkClosedSymChoice, nkOpenSymChoice}
nkStrKinds* = {nkStrLit..nkTripleStrLit} nkStrKinds* = {nkStrLit..nkTripleStrLit}
skLocalVars* = {skVar, skLet, skForVar, skParam, skResult} skLocalVars* = {skVar, skLet, skForVar, skParam, skResult}
skProcKinds* = {skProc, skTemplate, skMacro, skIterator, skProcKinds* = {skProc, skFunc, skTemplate, skMacro, skIterator,
skMethod, skConverter} skMethod, skConverter}
var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things 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) = proc mergeLoc(a: var TLoc, b: TLoc) =
if a.k == low(a.k): a.k = b.k 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 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.r == nil: a.r = b.r
#if a.a == 0: a.a = b.a
proc newSons*(father: PNode, length: int) = proc newSons*(father: PNode, length: int) =
if isNil(father.sons): if isNil(father.sons):

View file

@ -130,7 +130,7 @@ proc hashType(c: var MD5Context, t: PType) =
c.hashSym body.sym c.hashSym body.sym
for i in countup(1, sonsLen(t) - 2): for i in countup(1, sonsLen(t) - 2):
c.hashType t.sons[i] c.hashType t.sons[i]
of tyFromExpr, tyFieldAccessor: of tyFromExpr:
c.hashTree(t.n) c.hashTree(t.n)
of tyArray: of tyArray:
c.hashTree(t.sons[0].n) 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) if d.k == locNone: getTemp(p, typ.sons[0], d)
assert(d.t != nil) # generate an assignment to d: assert(d.t != nil) # generate an assignment to d:
var list: TLoc var list: TLoc
initLoc(list, locCall, d.t, OnUnknown) initLoc(list, locCall, d.lode, OnUnknown)
list.r = pl list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying genAssignment(p, d, list, {}) # no need for deep copying
else: 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) if d.k == locNone: getTemp(p, typ.sons[0], d)
assert(d.t != nil) # generate an assignment to d: assert(d.t != nil) # generate an assignment to d:
var list: TLoc var list: TLoc
initLoc(list, locCall, d.t, OnUnknown) initLoc(list, locCall, d.lode, OnUnknown)
list.r = callPattern % [op.r, pl, pl.addComma, rawProc] list.r = callPattern % [op.r, pl, pl.addComma, rawProc]
genAssignment(p, d, list, {}) # no need for deep copying genAssignment(p, d, list, {}) # no need for deep copying
else: 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) if d.k == locNone: getTemp(p, typ.sons[0], d)
assert(d.t != nil) # generate an assignment to d: assert(d.t != nil) # generate an assignment to d:
var list: TLoc var list: TLoc
initLoc(list, locCall, d.t, OnUnknown) initLoc(list, locCall, d.lode, OnUnknown)
list.r = pl list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying genAssignment(p, d, list, {}) # no need for deep copying
else: else:
@ -519,7 +519,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
if d.k == locNone: getTemp(p, typ.sons[0], d) if d.k == locNone: getTemp(p, typ.sons[0], d)
assert(d.t != nil) # generate an assignment to d: assert(d.t != nil) # generate an assignment to d:
var list: TLoc var list: TLoc
initLoc(list, locCall, nil, OnUnknown) initLoc(list, locCall, ri, OnUnknown)
list.r = pl list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying genAssignment(p, d, list, {}) # no need for deep copying
else: else:

View file

@ -157,10 +157,23 @@ proc getStorageLoc(n: PNode): TStorageLoc =
result = getStorageLoc(n.sons[0]) result = getStorageLoc(n.sons[0])
else: result = OnUnknown 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) = 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)) linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
elif dest.s == OnHeap: elif dest.storage == OnHeap:
# location is on heap # location is on heap
# now the writer barrier is inlined for performance: # 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 = proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc =
assert field != nil assert field != nil
result.k = locField result.k = locField
result.s = a.s result.storage = a.storage
result.t = t result.lode = lodeTyp t
result.r = rdLoc(a) & "." & field result.r = rdLoc(a) & "." & field
proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
let newflags = let newflags =
if src.s == OnStatic: if src.storage == OnStatic:
flags + {needToCopy} flags + {needToCopy}
elif tfShallow in dest.t.flags: elif tfShallow in dest.t.flags:
flags - {needToCopy} flags - {needToCopy}
@ -225,7 +238,7 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags,
t: PNode, typ: PType) = t: PNode, typ: PType) =
if t == nil: return if t == nil: return
let newflags = let newflags =
if src.s == OnStatic: if src.storage == OnStatic:
flags + {needToCopy} flags + {needToCopy}
elif tfShallow in dest.t.flags: elif tfShallow in dest.t.flags:
flags - {needToCopy} flags - {needToCopy}
@ -250,7 +263,7 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
# (for objects, etc.): # (for objects, etc.):
if needToCopy notin flags or if needToCopy notin flags or
tfShallow in skipTypes(dest.t, abstractVarRange).flags: tfShallow in skipTypes(dest.t, abstractVarRange).flags:
if dest.s == OnStack or not usesNativeGC(): if dest.storage == OnStack or not usesNativeGC():
useStringh(p.module) useStringh(p.module)
linefmt(p, cpsStmts, linefmt(p, cpsStmts,
"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", "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: of tyRef:
genRefAssign(p, dest, src, flags) genRefAssign(p, dest, src, flags)
of tySequence: 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) genRefAssign(p, dest, src, flags)
else: else:
linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n",
addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t)) addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t))
of tyString: 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) genRefAssign(p, dest, src, flags)
else: 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) 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: # we use a temporary to care for the dreaded self assignment:
var tmp: TLoc var tmp: TLoc
getTemp(p, ty, tmp) 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)) linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
else: internalError("genAssignment: " & $ty.kind) 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() #writeStackTrace()
#echo p.currLineInfo, " requesting" #echo p.currLineInfo, " requesting"
linefmt(p, cpsStmts, "#memTrackerWrite((void*)$1, $2, $3, $4);$n", linefmt(p, cpsStmts, "#memTrackerWrite((void*)$1, $2, $3, $4);$n",
@ -407,11 +420,11 @@ proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) =
else: else:
d = s # ``d`` is free, so fill it with ``s`` 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 var a: TLoc
if d.k != locNone: if d.k != locNone:
# need to generate an assignment here # need to generate an assignment here
initLoc(a, locData, t, OnStatic) initLoc(a, locData, n, OnStatic)
a.r = r a.r = r
if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
else: genAssignment(p, d, a, {needToCopy}) 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 # we cannot call initLoc() here as that would overwrite
# the flags field! # the flags field!
d.k = locData d.k = locData
d.t = t d.lode = n
d.r = r 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 var a: TLoc
if d.k != locNone: if d.k != locNone:
# need to generate an assignment here # need to generate an assignment here
initLoc(a, locExpr, t, s) initLoc(a, locExpr, n, s)
a.r = r a.r = r
if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
else: genAssignment(p, d, a, {needToCopy}) 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 # we cannot call initLoc() here as that would overwrite
# the flags field! # the flags field!
d.k = locExpr d.k = locExpr
d.t = t d.lode = n
d.r = r d.r = r
proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = 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) assert(e.sons[2].typ != nil)
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b) 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) = proc binaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
var a, b: TLoc 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) assert(e.sons[2].typ != nil)
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b) 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) = proc unaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) =
var a: TLoc var a: TLoc
initLocExpr(p, e.sons[1], a) 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) = proc unaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
var a: TLoc var a: TLoc
initLocExpr(p, e.sons[1], a) 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; proc binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc;
frmt: string): Rope = frmt: string): Rope =
@ -514,11 +527,11 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
let t = e.typ.skipTypes(abstractRange) let t = e.typ.skipTypes(abstractRange)
if optOverflowCheck notin p.options: if optOverflowCheck notin p.options:
let res = opr[m] % [getTypeDesc(p.module, e.typ), rdLoc(a), rdLoc(b)] 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: else:
let res = binaryArithOverflowRaw(p, t, a, b, let res = binaryArithOverflowRaw(p, t, a, b,
if t.kind == tyInt64: prc64[m] else: prc[m]) 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) = proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
const const
@ -535,7 +548,7 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
if optOverflowCheck in p.options: if optOverflowCheck in p.options:
linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n", linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n",
rdLoc(a), intLiteral(firstOrd(t))) 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) = proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
const 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 # BUGFIX: cannot use result-type here, as it may be a boolean
s = max(getSize(a.t), getSize(b.t)) * 8 s = max(getSize(a.t), getSize(b.t)) * 8
k = getSize(a.t) * 8 k = getSize(a.t) * 8
putIntoDest(p, d, e.typ, putIntoDest(p, d, e,
binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s), binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s),
getSimpleTypeDesc(p.module, e.typ), rope(k)]) 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[1], a)
initLocExpr(p, e.sons[2], b) initLocExpr(p, e.sons[2], b)
if a.t.skipTypes(abstractInst).callConv == ccClosure: 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)]) "($1.ClP_0 == $2.ClP_0 && $1.ClE_0 == $2.ClE_0)" % [rdLoc(a), rdLoc(b)])
else: 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) = proc genIsNil(p: BProc, e: PNode, d: var TLoc) =
let t = skipTypes(e.sons[1].typ, abstractRange) 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) assert(e.sons[1].typ != nil)
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
t = skipTypes(e.typ, abstractRange) t = skipTypes(e.typ, abstractRange)
putIntoDest(p, d, e.typ, putIntoDest(p, d, e,
unArithTab[op] % [rdLoc(a), rope(getSize(t) * 8), unArithTab[op] % [rdLoc(a), rope(getSize(t) * 8),
getSimpleTypeDesc(p.module, e.typ)]) 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)) # message(e.info, warnUser, "CAME HERE " & renderTree(e))
expr(p, e.sons[0], d) expr(p, e.sons[0], d)
if e.sons[0].typ.skipTypes(abstractInst).kind == tyRef: if e.sons[0].typ.skipTypes(abstractInst).kind == tyRef:
d.s = OnHeap d.storage = OnHeap
else: else:
var a: TLoc var a: TLoc
var typ = skipTypes(e.sons[0].typ, abstractInst) 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) initLocExprSingleUse(p, e.sons[0], a)
if d.k == locNone: if d.k == locNone:
# dest = *a; <-- We do not know that 'dest' is on the heap! # 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. # been assigned to.
case typ.kind case typ.kind
of tyRef: of tyRef:
d.s = OnHeap d.storage = OnHeap
of tyVar: of tyVar:
d.s = OnUnknown d.storage = OnUnknown
if tfVarIsPtr notin typ.flags and p.module.compileToCpp and if tfVarIsPtr notin typ.flags and p.module.compileToCpp and
e.kind == nkHiddenDeref: e.kind == nkHiddenDeref:
putIntoDest(p, d, e.typ, rdLoc(a), a.s) putIntoDest(p, d, e, rdLoc(a), a.storage)
return return
of tyPtr: of tyPtr:
d.s = OnUnknown # BUGFIX! d.storage = OnUnknown # BUGFIX!
else: else:
internalError(e.info, "genDeref " & $typ.kind) internalError(e.info, "genDeref " & $typ.kind)
elif p.module.compileToCpp: elif p.module.compileToCpp:
if typ.kind == tyVar and tfVarIsPtr notin typ.flags and if typ.kind == tyVar and tfVarIsPtr notin typ.flags and
e.kind == nkHiddenDeref: e.kind == nkHiddenDeref:
putIntoDest(p, d, e.typ, rdLoc(a), a.s) putIntoDest(p, d, e, rdLoc(a), a.storage)
return return
if enforceDeref and mt == ctPtrToArray: if enforceDeref and mt == ctPtrToArray:
# we lie about the type for better C interop: 'ptr array[3,T]' is # 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 # See tmissingderef. So we get rid of the deref instead. The codegen
# ends up using 'memcpy' for the array assignment, # ends up using 'memcpy' for the array assignment,
# so the '&' and '*' cancel out: # 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: 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) = proc genAddr(p: BProc, e: PNode, d: var TLoc) =
# careful 'addr(myptrToArray)' needs to get the ampersand: # careful 'addr(myptrToArray)' needs to get the ampersand:
if e.sons[0].typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}: if e.sons[0].typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
var a: TLoc var a: TLoc
initLocExpr(p, e.sons[0], a) 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 &") #Message(e.info, warnUser, "HERE NEW &")
elif mapType(e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ): elif mapType(e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ):
expr(p, e.sons[0], d) expr(p, e.sons[0], d)
else: else:
var a: TLoc var a: TLoc
initLocExpr(p, e.sons[0], a) 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) = 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) = proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc) =
initLocExpr(p, e.sons[0], a) 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) of nkIntLit..nkUInt64Lit: i = int(e.sons[1].intVal)
else: internalError(e.info, "genTupleElem") else: internalError(e.info, "genTupleElem")
addf(r, ".Field$1", [rope(i)]) 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; proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope;
resTyp: ptr PType = nil): PSym = 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 # we found a unique tuple type which lacks field information
# so we use Field$i # so we use Field$i
addf(r, ".Field$1", [rope(f.position)]) addf(r, ".Field$1", [rope(f.position)])
putIntoDest(p, d, f.typ, r, a.s) putIntoDest(p, d, e, r, a.storage)
else: else:
var rtyp: PType var rtyp: PType
let field = lookupFieldAgain(p, ty, f, r, addr rtyp) 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 and rtyp != nil: fillObjectFields(p.module, rtyp)
if field.loc.r == nil: internalError(e.info, "genRecordField 3 " & typeToString(ty)) if field.loc.r == nil: internalError(e.info, "genRecordField 3 " & typeToString(ty))
addf(r, ".$1", [field.loc.r]) addf(r, ".$1", [field.loc.r])
putIntoDest(p, d, field.typ, r, a.s) putIntoDest(p, d, e, r, a.storage)
#d.s = a.s
proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) 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] if op.magic == mNot: it = it.sons[1]
let disc = it.sons[2].skipConv let disc = it.sons[2].skipConv
assert(disc.kind == nkSym) assert(disc.kind == nkSym)
initLoc(test, locNone, it.typ, OnStack) initLoc(test, locNone, it, OnStack)
initLocExpr(p, it.sons[1], u) initLocExpr(p, it.sons[1], u)
var o = obj var o = obj
let d = lookupFieldAgain(p, origTy, disc.sym, o) let d = lookupFieldAgain(p, origTy, disc.sym, o)
initLoc(v, locExpr, d.typ, OnUnknown) initLoc(v, locExpr, disc, OnUnknown)
v.r = o v.r = o
v.r.add(".") v.r.add(".")
v.r.add(d.loc.r) 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: internalError(e.info, "genCheckedRecordField") # generate the checks:
genFieldCheck(p, e, r, field, ty) genFieldCheck(p, e, r, field, ty)
add(r, rfmt(nil, ".$1", field.loc.r)) 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: else:
genRecordField(p, e.sons[0], d) 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 var a, b: TLoc
initLocExpr(p, x, a) initLocExpr(p, x, a)
initLocExpr(p, y, b) 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): if idx < firstOrd(ty) or idx > lastOrd(ty):
localError(x.info, errIndexOutOfBounds) localError(x.info, errIndexOutOfBounds)
d.inheritLocation(a) d.inheritLocation(a)
putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), putIntoDest(p, d, n,
rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.s) 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 var a, b: TLoc
initLocExpr(p, x, a) initLocExpr(p, x, a)
initLocExpr(p, y, b) initLocExpr(p, y, b)
var ty = skipTypes(a.t, abstractVarRange) var ty = skipTypes(a.t, abstractVarRange)
if d.k == locNone: d.s = a.s inheritLocation(d, a)
putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), putIntoDest(p, d, n,
rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s) 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 var a, b: TLoc
initLocExpr(p, x, a) initLocExpr(p, x, a)
initLocExpr(p, y, b) # emit range check: initLocExpr(p, y, b) # emit range check:
if optBoundsCheck in p.options: if optBoundsCheck in p.options:
linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError();$n", linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError();$n",
rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``! rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``!
if d.k == locNone: d.s = a.s inheritLocation(d, a)
putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), putIntoDest(p, d, n,
rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s) 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 var a, b: TLoc
initLocExpr(p, x, a) initLocExpr(p, x, a)
initLocExpr(p, y, b) initLocExpr(p, y, b)
@ -892,20 +904,20 @@ proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) =
linefmt(p, cpsStmts, linefmt(p, cpsStmts,
"if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n", "if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
rdLoc(b), rdLoc(a), lenField(p)) 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}: if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
a.r = rfmt(nil, "(*$1)", a.r) a.r = rfmt(nil, "(*$1)", a.r)
putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), putIntoDest(p, d, n,
rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.s) rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.storage)
proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) = proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
var ty = skipTypes(n.sons[0].typ, abstractVarRange + tyUserTypeClasses) var ty = skipTypes(n.sons[0].typ, abstractVarRange + tyUserTypeClasses)
if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange)
case ty.kind case ty.kind
of tyArray: genArrayElem(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.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.sons[0], n.sons[1], d) of tySequence, tyString: genSeqElem(p, n, n.sons[0], n.sons[1], d)
of tyCString: genCStringElem(p, 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) of tyTuple: genTupleElem(p, n, d)
else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') 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)]) getTypeDesc(p.module, bt)])
#if bt != b.t: #if bt != b.t:
# echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(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) getIntTemp(p, tmpL)
lineCg(p, cpsStmts, "$1 = $2->$3++;$n", tmpL.r, rdLoc(a), lenField(p)) lineCg(p, cpsStmts, "$1 = $2->$3++;$n", tmpL.r, rdLoc(a), lenField(p))
dest.r = rfmt(nil, "$1->data[$2]", rdLoc(a), tmpL.r) 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 var sizeExpr = sizeExpr
let typ = a.t let typ = a.t
var b: TLoc var b: TLoc
initLoc(b, locExpr, a.t, OnHeap) initLoc(b, locExpr, a.lode, OnHeap)
let refType = typ.skipTypes(abstractInst) let refType = typ.skipTypes(abstractInst)
assert refType.kind == tyRef assert refType.kind == tyRef
let bt = refType.lastSon let bt = refType.lastSon
@ -1098,7 +1110,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
let args = [getTypeDesc(p.module, typ), let args = [getTypeDesc(p.module, typ),
genTypeInfo(p.module, typ), genTypeInfo(p.module, typ),
sizeExpr] sizeExpr]
if a.s == OnHeap and usesNativeGC(): if a.storage == OnHeap and usesNativeGC():
# use newObjRC1 as an optimization # use newObjRC1 as an optimization
if canFormAcycle(a.t): if canFormAcycle(a.t):
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc) 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), let args = [getTypeDesc(p.module, seqtype),
genTypeInfo(p.module, seqtype), length] genTypeInfo(p.module, seqtype), length]
var call: TLoc var call: TLoc
initLoc(call, locExpr, dest.t, OnHeap) initLoc(call, locExpr, dest.lode, OnHeap)
if dest.s == OnHeap and usesNativeGC(): if dest.storage == OnHeap and usesNativeGC():
if canFormAcycle(dest.t): if canFormAcycle(dest.t):
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc) linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc)
else: else:
@ -1151,7 +1163,7 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
let seqtype = skipTypes(e.typ, abstractVarRange) let seqtype = skipTypes(e.typ, abstractVarRange)
var a: TLoc var a: TLoc
initLocExpr(p, e.sons[1], a) 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)", [ "($1)#nimNewSeqOfCap($2, $3)", [
getTypeDesc(p.module, seqtype), getTypeDesc(p.module, seqtype),
genTypeInfo(p.module, seqtype), a.rdLoc])) genTypeInfo(p.module, seqtype), a.rdLoc]))
@ -1163,7 +1175,7 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
let t = n.typ let t = n.typ
discard getTypeDesc(p.module, t) # so that any fields are initialized discard getTypeDesc(p.module, t) # so that any fields are initialized
let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) 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: if id == p.module.labels:
# expression not found in the cache: # expression not found in the cache:
inc(p.module.labels) inc(p.module.labels)
@ -1205,8 +1217,8 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
add(tmp2.r, ".") add(tmp2.r, ".")
add(tmp2.r, field.loc.r) add(tmp2.r, field.loc.r)
tmp2.k = locTemp tmp2.k = locTemp
tmp2.t = field.loc.t tmp2.lode = it.sons[1]
tmp2.s = if isRef: OnHeap else: OnStack tmp2.storage = if isRef: OnHeap else: OnStack
expr(p, it.sons[1], tmp2) expr(p, it.sons[1], tmp2)
if d.k == locNone: if d.k == locNone:
@ -1214,37 +1226,37 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
else: else:
genAssignment(p, d, tmp, {}) 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 var arr: TLoc
if d.k == locNone: if d.k == locNone:
getTemp(p, t.typ, d) getTemp(p, n.typ, d)
# generate call to newSeq before adding the elements per hand: # generate call to newSeq before adding the elements per hand:
genNewSeqAux(p, d, intLiteral(sonsLen(t))) genNewSeqAux(p, d, intLiteral(sonsLen(n)))
for i in countup(0, sonsLen(t) - 1): for i in countup(0, sonsLen(n) - 1):
initLoc(arr, locExpr, elemType(skipTypes(t.typ, typedescInst)), OnHeap) initLoc(arr, locExpr, n[i], OnHeap)
arr.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i)) arr.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i))
arr.s = OnHeap # we know that sequences are on the heap arr.storage = OnHeap # we know that sequences are on the heap
expr(p, t.sons[i], arr) expr(p, n[i], arr)
gcUsage(t) 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 var elem, a, arr: TLoc
if t.sons[1].kind == nkBracket: if n.sons[1].kind == nkBracket:
t.sons[1].typ = t.typ n.sons[1].typ = n.typ
genSeqConstr(p, t.sons[1], d) genSeqConstr(p, n.sons[1], d)
return return
if d.k == locNone: if d.k == locNone:
getTemp(p, t.typ, d) getTemp(p, n.typ, d)
# generate call to newSeq before adding the elements per hand: # generate call to newSeq before adding the elements per hand:
var L = int(lengthOrd(t.sons[1].typ)) var L = int(lengthOrd(n.sons[1].typ))
genNewSeqAux(p, d, intLiteral(L)) genNewSeqAux(p, d, intLiteral(L))
initLocExpr(p, t.sons[1], a) initLocExpr(p, n.sons[1], a)
for i in countup(0, L - 1): 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.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i))
elem.s = OnHeap # we know that sequences are on the heap elem.storage = OnHeap # we know that sequences are on the heap
initLoc(arr, locExpr, elemType(skipTypes(t.sons[1].typ, abstractInst)), a.s) initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage)
arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i)) arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i))
genAssignment(p, elem, arr, {afDestIsNil, needToCopy}) genAssignment(p, elem, arr, {afDestIsNil, needToCopy})
@ -1256,7 +1268,7 @@ proc genNewFinalize(p: BProc, e: PNode) =
refType = skipTypes(e.sons[1].typ, abstractVarRange) refType = skipTypes(e.sons[1].typ, abstractVarRange)
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], f) initLocExpr(p, e.sons[2], f)
initLoc(b, locExpr, a.t, OnHeap) initLoc(b, locExpr, a.lode, OnHeap)
ti = genTypeInfo(p.module, refType) ti = genTypeInfo(p.module, refType)
addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [ 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)) r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r))
else: else:
r = rfmt(p.module, "($1)", genOfHelper(p, dest, r)) 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) = proc genOf(p: BProc, n: PNode, d: var TLoc) =
genOf(p, n.sons[1], n.sons[2].typ, d) 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) var t = skipTypes(e.sons[1].typ, abstractVarRange)
case t.kind case t.kind
of tyInt..tyInt64, tyUInt..tyUInt64: of tyInt..tyInt64, tyUInt..tyUInt64:
putIntoDest(p, d, e.typ, putIntoDest(p, d, e,
ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]), a.s) ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]), a.storage)
of tyFloat..tyFloat128: 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: 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: 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: of tyEnum, tyOrdinal:
putIntoDest(p, d, e.typ, putIntoDest(p, d, e,
ropecg(p.module, "#reprEnum((NI)$1, $2)", [ 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: 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: of tySet:
putIntoDest(p, d, e.typ, ropecg(p.module, "#reprSet($1, $2)", [ putIntoDest(p, d, e, ropecg(p.module, "#reprSet($1, $2)", [
addrLoc(a), genTypeInfo(p.module, t)]), a.s) addrLoc(a), genTypeInfo(p.module, t)]), a.storage)
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
var b: TLoc var b: TLoc
case a.t.kind case a.t.kind
of tyOpenArray, tyVarargs: 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: of tyString, tySequence:
putIntoDest(p, b, e.typ, putIntoDest(p, b, e,
"$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.s) "$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.storage)
of tyArray: of tyArray:
putIntoDest(p, b, e.typ, putIntoDest(p, b, e,
"$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.s) "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.storage)
else: internalError(e.sons[0].info, "genRepr()") else: internalError(e.sons[0].info, "genRepr()")
putIntoDest(p, d, e.typ, putIntoDest(p, d, e,
ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b), 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: of tyCString, tyArray, tyRef, tyPtr, tyPointer, tyNil, tySequence:
putIntoDest(p, d, e.typ, putIntoDest(p, d, e,
ropecg(p.module, "#reprAny($1, $2)", [ 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: of tyEmpty, tyVoid:
localError(e.info, "'repr' doesn't support 'void' type") localError(e.info, "'repr' doesn't support 'void' type")
else: else:
putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)", putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)",
[addrLoc(a), genTypeInfo(p.module, t)]), a.s) [addrLoc(a), genTypeInfo(p.module, t)]),
a.storage)
gcUsage(e) gcUsage(e)
proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) = proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) =
let t = e.sons[1].typ 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) = proc genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) =
var a: TLoc var a: TLoc
@ -1409,11 +1422,11 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
else: else:
frmt = "$1 = ($2 ? $2->len : 0);$n" frmt = "$1 = ($2 ? $2->len : 0);$n"
lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a)) lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a))
putIntoDest(p, d, e.typ, tmp.r) putIntoDest(p, d, e, tmp.r)
of tyArray: of tyArray:
# YYY: length(sideeffect) is optimized away incorrectly? # YYY: length(sideeffect) is optimized away incorrectly?
if op == mHigh: putIntoDest(p, d, e.typ, rope(lastOrd(typ))) if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(typ)))
else: putIntoDest(p, d, e.typ, rope(lengthOrd(typ))) else: putIntoDest(p, d, e, rope(lengthOrd(typ)))
else: internalError(e.info, "genArrayLen()") else: internalError(e.info, "genArrayLen()")
proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = 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 result = sonsLen(s) <= 8 # 8 seems to be a good value
proc binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) = 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) = proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) =
case int(getSize(skipTypes(e.sons[1].typ, abstractVar))) case int(getSize(skipTypes(e.sons[1].typ, abstractVar)))
@ -1500,7 +1513,7 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) =
else: else:
e.sons[2] e.sons[2]
initLocExpr(p, ea, a) initLocExpr(p, ea, a)
initLoc(b, locExpr, e.typ, OnUnknown) initLoc(b, locExpr, e, OnUnknown)
b.r = rope("(") b.r = rope("(")
var length = sonsLen(e.sons[1]) var length = sonsLen(e.sons[1])
for i in countup(0, length - 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)]) addf(b.r, "$1 == $2", [rdCharLoc(a), rdCharLoc(x)])
if i < length - 1: add(b.r, " || ") if i < length - 1: add(b.r, " || ")
add(b.r, ")") add(b.r, ")")
putIntoDest(p, d, e.typ, b.r) putIntoDest(p, d, e, b.r)
else: else:
assert(e.sons[1].typ != nil) assert(e.sons[1].typ != nil)
assert(e.sons[2].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) initLocExpr(p, e.sons[1], a)
let etyp = skipTypes(e.typ, abstractRange) let etyp = skipTypes(e.typ, abstractRange)
if etyp.kind in ValueTypes and lfIndirect notin a.flags: if etyp.kind in ValueTypes and lfIndirect notin a.flags:
putIntoDest(p, d, e.typ, "(*($1*) ($2))" % putIntoDest(p, d, e, "(*($1*) ($2))" %
[getTypeDesc(p.module, e.typ), addrLoc(a)], a.s) [getTypeDesc(p.module, e.typ), addrLoc(a)], a.storage)
elif etyp.kind == tyProc and etyp.callConv == ccClosure: elif etyp.kind == tyProc and etyp.callConv == ccClosure:
putIntoDest(p, d, e.typ, "(($1) ($2))" % putIntoDest(p, d, e, "(($1) ($2))" %
[getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.s) [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage)
else: else:
putIntoDest(p, d, e.typ, "(($1) ($2))" % putIntoDest(p, d, e, "(($1) ($2))" %
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.s) [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
proc genCast(p: BProc, e: PNode, d: var TLoc) = proc genCast(p: BProc, e: PNode, d: var TLoc) =
const ValueTypes = {tyFloat..tyFloat128, tyTuple, tyObject, tyArray} 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", linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n",
getTypeDesc(p.module, e.sons[1].typ), getTypeDesc(p.module, e.typ), lbl) getTypeDesc(p.module, e.sons[1].typ), getTypeDesc(p.module, e.typ), lbl)
tmp.k = locExpr tmp.k = locExpr
tmp.t = srct tmp.lode = lodeTyp srct
tmp.s = OnStack tmp.storage = OnStack
tmp.flags = {} tmp.flags = {}
expr(p, e.sons[1], tmp) 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: else:
# I prefer the shorter cast version for pointer types -> generate less # I prefer the shorter cast version for pointer types -> generate less
# C code; plus it's the right thing to do for closures: # 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 if optRangeCheck notin p.options or dest.skipTypes({tyRange}).kind in
{tyUInt..tyUInt64}: {tyUInt..tyUInt64}:
initLocExpr(p, n.sons[0], a) initLocExpr(p, n.sons[0], a)
putIntoDest(p, d, n.typ, "(($1) ($2))" % putIntoDest(p, d, n, "(($1) ($2))" %
[getTypeDesc(p.module, dest), rdCharLoc(a)], a.s) [getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)
else: else:
initLocExpr(p, n.sons[0], a) 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), getTypeDesc(p.module, dest), rdCharLoc(a),
genLiteral(p, n.sons[1], dest), genLiteral(p, n.sons[2], dest), 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) = proc genConv(p: BProc, e: PNode, d: var TLoc) =
let destType = e.typ.skipTypes({tyVar, tyGenericInst, tyAlias}) let destType = e.typ.skipTypes({tyVar, tyGenericInst, tyAlias})
@ -1658,13 +1671,15 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) =
proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc var a: TLoc
initLocExpr(p, n.sons[0], a) 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) = proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc var a: TLoc
initLocExpr(p, n.sons[0], a) initLocExpr(p, n.sons[0], a)
putIntoDest(p, d, skipTypes(n.typ, abstractVar), putIntoDest(p, d, n,
ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), a.s) ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]),
a.storage)
gcUsage(n) gcUsage(n)
proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = 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)") binaryExpr(p, e, d, "($1 == $2)")
elif (a.kind in {nkStrLit..nkTripleStrLit}) and (a.strVal == ""): elif (a.kind in {nkStrLit..nkTripleStrLit}) and (a.strVal == ""):
initLocExpr(p, e.sons[2], x) 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))) rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p)))
elif (b.kind in {nkStrLit..nkTripleStrLit}) and (b.strVal == ""): elif (b.kind in {nkStrLit..nkTripleStrLit}) and (b.strVal == ""):
initLocExpr(p, e.sons[1], x) 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))) rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p)))
else: else:
binaryExpr(p, e, d, "#eqStrings($1, $2)") 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) assert(e.sons[2].typ != nil)
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b) initLocExpr(p, e.sons[2], b)
putIntoDest(p, d, e.typ, rfmt(nil, "(($4)($2) $1 ($4)($3))", putIntoDest(p, d, e, rfmt(nil, "(($4)($2) $1 ($4)($3))",
rope(opr[m]), rdLoc(a), rdLoc(b), rope(opr[m]), rdLoc(a), rdLoc(b),
getSimpleTypeDesc(p.module, e[1].typ))) getSimpleTypeDesc(p.module, e[1].typ)))
if optNaNCheck in p.options: if optNaNCheck in p.options:
linefmt(p, cpsStmts, "#nanCheck($1);$n", rdLoc(d)) linefmt(p, cpsStmts, "#nanCheck($1);$n", rdLoc(d))
if optInfCheck in p.options: 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 ranged = skipTypes(e.sons[1].typ, {tyGenericInst, tyAlias, tyVar})
let res = binaryArithOverflowRaw(p, ranged, a, b, let res = binaryArithOverflowRaw(p, ranged, a, b,
if underlying.kind == tyInt64: fun64[op] else: fun[op]) if underlying.kind == tyInt64: fun64[op] else: fun[op])
putIntoDest(p, a, ranged, "($#)($#)" % [ putIntoDest(p, a, e.sons[1], "($#)($#)" % [
getTypeDesc(p.module, ranged), res]) getTypeDesc(p.module, ranged), res])
of mConStrStr: genStrConcat(p, e, d) 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 mNewSeqOfCap: genNewSeqOfCap(p, e, d)
of mSizeOf: of mSizeOf:
let t = e.sons[1].typ.skipTypes({tyTypeDesc}) 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 mChr: genSomeCast(p, e, d)
of mOrd: genOrd(p, e, d) of mOrd: genOrd(p, e, d)
of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray: of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray:
@ -1783,7 +1798,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
else: else:
frmt = "$1 = $2->len;$n" frmt = "$1 = $2->len;$n"
lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a)) 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 mGCref: unaryStmt(p, e, d, "#nimGCref($1);$n")
of mGCunref: unaryStmt(p, e, d, "#nimGCunref($1);$n") of mGCunref: unaryStmt(p, e, d, "#nimGCunref($1);$n")
of mSetLengthStr: genSetLengthStr(p, e, d) of mSetLengthStr: genSetLengthStr(p, e, d)
@ -1825,7 +1840,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
var var
a, b, idx: TLoc a, b, idx: TLoc
if nfAllConst in e.flags: if nfAllConst in e.flags:
putIntoDest(p, d, e.typ, genSetNode(p, e)) putIntoDest(p, d, e, genSetNode(p, e))
else: else:
if d.k == locNone: getTemp(p, e.typ, d) if d.k == locNone: getTemp(p, e.typ, d)
if getSize(e.typ) > 8: 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): for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i] var it = n.sons[i]
if it.kind == nkExprColonExpr: it = it.sons[1] 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)] rec.r = "$1.Field$2" % [rdLoc(d), rope(i)]
expr(p, it, rec) expr(p, it, rec)
@ -1888,7 +1903,7 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) =
var tmp = "CNSTCLOSURE" & rope(p.module.labels) var tmp = "CNSTCLOSURE" & rope(p.module.labels)
addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n", addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n",
[getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)]) [getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)])
putIntoDest(p, d, n.typ, tmp, OnStatic) putIntoDest(p, d, n, tmp, OnStatic)
else: else:
var tmp, a, b: TLoc var tmp, a, b: TLoc
initLocExpr(p, n.sons[0], a) 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 not handleConstExpr(p, n, d):
if d.k == locNone: getTemp(p, n.typ, d) if d.k == locNone: getTemp(p, n.typ, d)
for i in countup(0, sonsLen(n) - 1): 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)] arr.r = "$1[$2]" % [rdLoc(d), intLiteral(i)]
expr(p, n.sons[i], arr) 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", linefmt(p, cpsStmts, "#chckObj($1.m_type, $2);$n",
r, genTypeInfo(p.module, dest)) r, genTypeInfo(p.module, dest))
if n.sons[0].typ.kind != tyObject: if n.sons[0].typ.kind != tyObject:
putIntoDest(p, d, n.typ, putIntoDest(p, d, n,
"(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.s) "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
else: else:
putIntoDest(p, d, n.typ, "(*($1*) ($2))" % putIntoDest(p, d, n, "(*($1*) ($2))" %
[getTypeDesc(p.module, dest), addrLoc(a)], a.s) [getTypeDesc(p.module, dest), addrLoc(a)], a.storage)
proc downConv(p: BProc, n: PNode, d: var TLoc) = proc downConv(p: BProc, n: PNode, d: var TLoc) =
if p.module.compileToCpp: 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) linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r)
else: else:
r = "&" & r r = "&" & r
putIntoDest(p, d, n.typ, r, a.s) putIntoDest(p, d, n, r, a.storage)
else: 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) = proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
let t = n.typ 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)]) [getTypeDesc(p.module, t), tmp, genConstExpr(p, n)])
if d.k == locNone: if d.k == locNone:
fillLoc(d, locData, t, tmp, OnStatic) fillLoc(d, locData, n, tmp, OnStatic)
else: else:
putDataIntoDest(p, d, t, tmp) putDataIntoDest(p, d, n, tmp)
# This fixes bug #4551, but we really need better dataflow # This fixes bug #4551, but we really need better dataflow
# analysis to make this 100% safe. # analysis to make this 100% safe.
if t.kind notin {tySequence, tyString}: if t.kind notin {tySequence, tyString}:
d.s = OnStatic d.storage = OnStatic
proc expr(p: BProc, n: PNode, d: var TLoc) = proc expr(p: BProc, n: PNode, d: var TLoc) =
p.currLineInfo = n.info p.currLineInfo = n.info
@ -2019,31 +2034,31 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of skMethod: of skMethod:
if {sfDispatcher, sfForward} * sym.flags != {}: if {sfDispatcher, sfForward} * sym.flags != {}:
# we cannot produce code for the dispatcher yet: # we cannot produce code for the dispatcher yet:
fillProcLoc(p.module, sym) fillProcLoc(p.module, n)
genProcPrototype(p.module, sym) genProcPrototype(p.module, sym)
else: else:
genProc(p.module, sym) genProc(p.module, sym)
putLocIntoDest(p, d, sym.loc) putLocIntoDest(p, d, sym.loc)
of skProc, skConverter, skIterator: of skProc, skConverter, skIterator, skFunc:
#if sym.kind == skIterator: #if sym.kind == skIterator:
# echo renderTree(sym.getBody, {renderIds}) # echo renderTree(sym.getBody, {renderIds})
if sfCompileTime in sym.flags: if sfCompileTime in sym.flags:
localError(n.info, "request to generate code for .compileTime proc: " & localError(n.info, "request to generate code for .compileTime proc: " &
sym.name.s) sym.name.s)
genProc(p.module, 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) internalError(n.info, "expr: proc not init " & sym.name.s)
putLocIntoDest(p, d, sym.loc) putLocIntoDest(p, d, sym.loc)
of skConst: of skConst:
if isSimpleConst(sym.typ): 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: else:
genComplexConst(p, sym, d) genComplexConst(p, sym, d)
of skEnumField: of skEnumField:
putIntoDest(p, d, n.typ, rope(sym.position)) putIntoDest(p, d, n, rope(sym.position))
of skVar, skForVar, skResult, skLet: of skVar, skForVar, skResult, skLet:
if {sfGlobal, sfThread} * sym.flags != {}: if {sfGlobal, sfThread} * sym.flags != {}:
genVarPrototype(p.module, sym) genVarPrototype(p.module, n)
if sym.loc.r == nil or sym.loc.t == nil: if sym.loc.r == nil or sym.loc.t == nil:
#echo "FAILED FOR PRCO ", p.prc.name.s #echo "FAILED FOR PRCO ", p.prc.name.s
#echo renderTree(p.prc.ast, {renderIds}) #echo renderTree(p.prc.ast, {renderIds})
@ -2051,7 +2066,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
if sfThread in sym.flags: if sfThread in sym.flags:
accessThreadLocalVar(p, sym) accessThreadLocalVar(p, sym)
if emulatedThreadVars(): if emulatedThreadVars():
putIntoDest(p, d, sym.loc.t, "NimTV_->" & sym.loc.r) putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.r)
else: else:
putLocIntoDest(p, d, sym.loc) putLocIntoDest(p, d, sym.loc)
else: else:
@ -2072,12 +2087,12 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
else: internalError(n.info, "expr(" & $sym.kind & "); unknown symbol") else: internalError(n.info, "expr(" & $sym.kind & "); unknown symbol")
of nkNilLit: of nkNilLit:
if not isEmptyType(n.typ): if not isEmptyType(n.typ):
putIntoDest(p, d, n.typ, genLiteral(p, n)) putIntoDest(p, d, n, genLiteral(p, n))
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
putDataIntoDest(p, d, n.typ, genLiteral(p, n)) putDataIntoDest(p, d, n, genLiteral(p, n))
of nkIntLit..nkUInt64Lit, of nkIntLit..nkUInt64Lit,
nkFloatLit..nkFloat128Lit, nkCharLit: 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, of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand,
nkCallStrLit: nkCallStrLit:
genLineDir(p, n) genLineDir(p, n)
@ -2097,7 +2112,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
genCall(p, n, d) genCall(p, n, d)
of nkCurly: of nkCurly:
if isDeepConstExpr(n) and n.len != 0: if isDeepConstExpr(n) and n.len != 0:
putIntoDest(p, d, n.typ, genSetNode(p, n)) putIntoDest(p, d, n, genSetNode(p, n))
else: else:
genSetConstr(p, n, d) genSetConstr(p, n, d)
of nkBracket: of nkBracket:
@ -2138,7 +2153,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of nkLambdaKinds: of nkLambdaKinds:
var sym = n.sons[namePos].sym var sym = n.sons[namePos].sym
genProc(p.module, 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) internalError(n.info, "expr: proc not init " & sym.name.s)
putLocIntoDest(p, d, sym.loc) putLocIntoDest(p, d, sym.loc)
of nkClosure: genClosure(p, n, d) of nkClosure: genClosure(p, n, d)
@ -2190,7 +2205,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
discard discard
of nkPragma: genPragma(p, n) of nkPragma: genPragma(p, n)
of nkPragmaBlock: expr(p, n.lastSon, d) of nkPragmaBlock: expr(p, n.lastSon, d)
of nkProcDef, nkMethodDef, nkConverterDef: of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef:
if n.sons[genericParamsPos].kind == nkEmpty: if n.sons[genericParamsPos].kind == nkEmpty:
var prc = n.sons[namePos].sym var prc = n.sons[namePos].sym
# due to a bug/limitation in the lambda lifting, unused inner procs # 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) initLocExpr(p, n.sons[L-1], tup)
var t = tup.t.skipTypes(abstractInst) var t = tup.t.skipTypes(abstractInst)
for i in countup(0, L-3): 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 sfCompileTime in v.flags: continue
if sfGlobal in v.flags: if sfGlobal in v.flags:
assignGlobalVar(p, v) assignGlobalVar(p, vn)
genObjectInit(p, cpsInit, v.typ, v.loc, true) genObjectInit(p, cpsInit, v.typ, v.loc, true)
registerGcRoot(p, v) registerGcRoot(p, v)
else: else:
assignLocalVar(p, v) assignLocalVar(p, vn)
initLocalVar(p, v, immediateAsgn=isAssignedImmediately(n[L-1])) 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: if t.kind == tyTuple:
field.r = "$1.Field$2" % [rdLoc(tup), rope(i)] field.r = "$1.Field$2" % [rdLoc(tup), rope(i)]
else: 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 ((((NI*) $1.ClE_0)[1]) < 0) break;$n", [rdLoc(a)])
# lineF(p, cpsStmts, "if (($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) = proc genGotoVar(p: BProc; value: PNode) =
if value.kind notin {nkCharLit..nkUInt64Lit}: 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]) lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
proc genSingleVar(p: BProc, a: PNode) = 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 sfCompileTime in v.flags: return
if sfGoto in v.flags: if sfGoto in v.flags:
# translate 'var state {.goto.} = X' into 'goto LX': # translate 'var state {.goto.} = X' into 'goto LX':
@ -195,7 +197,7 @@ proc genSingleVar(p: BProc, a: PNode) =
if sfPure in v.flags: if sfPure in v.flags:
# v.owner.kind != skModule: # v.owner.kind != skModule:
targetProc = p.module.preInitProc targetProc = p.module.preInitProc
assignGlobalVar(targetProc, v) assignGlobalVar(targetProc, vn)
# XXX: be careful here. # XXX: be careful here.
# Global variables should not be zeromem-ed within loops # Global variables should not be zeromem-ed within loops
# (see bug #20). # (see bug #20).
@ -206,7 +208,7 @@ proc genSingleVar(p: BProc, a: PNode) =
# Alternative construction using default constructor (which may zeromem): # Alternative construction using default constructor (which may zeromem):
# if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc) # if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc)
if sfExportc in v.flags and p.module.g.generatedHeader != nil: 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) registerGcRoot(p, v)
else: else:
let value = a.sons[2] let value = a.sons[2]
@ -217,7 +219,7 @@ proc genSingleVar(p: BProc, a: PNode) =
# parameterless constructor followed by an assignment operator. So we # parameterless constructor followed by an assignment operator. So we
# generate better code here: # generate better code here:
genLineDir(p, a) genLineDir(p, a)
let decl = localVarDecl(p, v) let decl = localVarDecl(p, vn)
var tmp: TLoc var tmp: TLoc
if value.kind in nkCallKinds and value[0].kind == nkSym and if value.kind in nkCallKinds and value[0].kind == nkSym and
sfConstructor in value[0].sym.flags: sfConstructor in value[0].sym.flags:
@ -233,7 +235,7 @@ proc genSingleVar(p: BProc, a: PNode) =
initLocExprSingleUse(p, value, tmp) initLocExprSingleUse(p, value, tmp)
lineF(p, cpsStmts, "$# = $#;$n", [decl, tmp.rdLoc]) lineF(p, cpsStmts, "$# = $#;$n", [decl, tmp.rdLoc])
return return
assignLocalVar(p, v) assignLocalVar(p, vn)
initLocalVar(p, v, imm) initLocalVar(p, v, imm)
if a.sons[2].kind != nkEmpty: if a.sons[2].kind != nkEmpty:
@ -513,7 +515,7 @@ proc genParForStmt(p: BProc, t: PNode) =
preserveBreakIdx: preserveBreakIdx:
let forLoopVar = t.sons[0].sym let forLoopVar = t.sons[0].sym
var rangeA, rangeB: TLoc var rangeA, rangeB: TLoc
assignLocalVar(p, forLoopVar) assignLocalVar(p, t.sons[0])
#initLoc(forLoopVar.loc, locLocalVar, forLoopVar.typ, onStack) #initLoc(forLoopVar.loc, locLocalVar, forLoopVar.typ, onStack)
#discard mangleName(forLoopVar) #discard mangleName(forLoopVar)
let call = t.sons[1] let call = t.sons[1]
@ -958,7 +960,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
res.add(t.sons[i].strVal) res.add(t.sons[i].strVal)
of nkSym: of nkSym:
var sym = t.sons[i].sym var sym = t.sons[i].sym
if sym.kind in {skProc, skIterator, skMethod}: if sym.kind in {skProc, skFunc, skIterator, skMethod}:
var a: TLoc var a: TLoc
initLocExpr(p, t.sons[i], a) initLocExpr(p, t.sons[i], a)
res.add($rdLoc(a)) res.add($rdLoc(a))

View file

@ -121,7 +121,8 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash;
var c: TTraversalClosure var c: TTraversalClosure
var p = newProc(nil, m) var p = newProc(nil, m)
result = "Marker_" & getTypeName(m, origTyp, sig) 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 case reason
of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n" of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n"

View file

@ -183,7 +183,7 @@ proc mapType(typ: PType): TCTypeKind =
of 8: result = ctInt64 of 8: result = ctInt64
else: internalError("mapType") else: internalError("mapType")
of tyRange: result = mapType(typ.sons[0]) of tyRange: result = mapType(typ.sons[0])
of tyPtr, tyVar, tyRef: of tyPtr, tyVar, tyRef, tyOptAsRef:
var base = skipTypes(typ.lastSon, typedescInst) var base = skipTypes(typ.lastSon, typedescInst)
case base.kind case base.kind
of tyOpenArray, tyArray, tyVarargs: result = ctPtrToArray of tyOpenArray, tyArray, tyVarargs: result = ctPtrToArray
@ -194,6 +194,13 @@ proc mapType(typ: PType): TCTypeKind =
else: result = ctPtr else: result = ctPtr
of tyPointer: result = ctPtr of tyPointer: result = ctPtr
of tySequence: result = ctNimSeq 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 tyProc: result = if typ.callConv != ccClosure: ctProc else: ctStruct
of tyString: result = ctNimStr of tyString: result = ctNimStr
of tyCString: result = ctCString of tyCString: result = ctCString
@ -282,12 +289,13 @@ proc ccgIntroducedPtr(s: PSym): bool =
result = (getSize(pt) > platform.floatSize*2) or (optByRef in s.options) result = (getSize(pt) > platform.floatSize*2) or (optByRef in s.options)
else: result = false else: result = false
proc fillResult(param: PSym) = proc fillResult(param: PNode) =
fillLoc(param.loc, locParam, param.typ, ~"Result", fillLoc(param.sym.loc, locParam, param, ~"Result",
OnStack) OnStack)
if mapReturnType(param.typ) != ctArray and isInvalidReturnType(param.typ): let t = param.sym.typ
incl(param.loc.flags, lfIndirect) if mapReturnType(t) != ctArray and isInvalidReturnType(t):
param.loc.s = OnUnknown incl(param.sym.loc.flags, lfIndirect)
param.sym.loc.storage = OnUnknown
proc typeNameOrLiteral(m: BModule; t: PType, literal: string): Rope = proc typeNameOrLiteral(m: BModule; t: PType, literal: string): Rope =
if t.sym != nil and sfImportc in t.sym.flags and t.sym.magic == mNone: 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 if result != nil: return
result = getTypePre(m, typ, sig) result = getTypePre(m, typ, sig)
if result != nil: return if result != nil: return
let concrete = typ.skipTypes(abstractInst) let concrete = typ.skipTypes(abstractInst + {tyOpt})
case concrete.kind case concrete.kind
of tySequence, tyTuple, tyObject: of tySequence, tyTuple, tyObject:
result = getTypeName(m, typ, sig) result = getTypeName(m, typ, sig)
@ -375,6 +383,12 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
of tySequence: of tySequence:
result = getTypeForward(m, t, hashType(t)) & "*" result = getTypeForward(m, t, hashType(t)) & "*"
pushType(m, 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: else:
result = getTypeDescAux(m, t, check) 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 var param = t.n.sons[i].sym
if isCompileTimeOnly(param.typ): continue if isCompileTimeOnly(param.typ): continue
if params != nil: add(params, ~", ") 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) param.paramStorageLoc)
if ccgIntroducedPtr(param): if ccgIntroducedPtr(param):
add(params, getTypeDescWeak(m, param.typ, check)) add(params, getTypeDescWeak(m, param.typ, check))
add(params, ~"*") add(params, ~"*")
incl(param.loc.flags, lfIndirect) incl(param.loc.flags, lfIndirect)
param.loc.s = OnUnknown param.loc.storage = OnUnknown
elif weakDep: elif weakDep:
add(params, getTypeDescWeak(m, param.typ, check)) add(params, getTypeDescWeak(m, param.typ, check))
else: else:
@ -417,7 +431,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
var j = 0 var j = 0
while arr.kind in {tyOpenArray, tyVarargs}: while arr.kind in {tyOpenArray, tyVarargs}:
# this fixes the 'sort' bug: # this fixes the 'sort' bug:
if param.typ.kind == tyVar: param.loc.s = OnUnknown if param.typ.kind == tyVar: param.loc.storage = OnUnknown
# need to pass hidden parameter: # need to pass hidden parameter:
addf(params, ", NI $1Len_$2", [param.loc.r, j.rope]) addf(params, ", NI $1Len_$2", [param.loc.r, j.rope])
inc(j) inc(j)
@ -496,16 +510,16 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
let sname = mangleRecFieldName(m, field, rectype) let sname = mangleRecFieldName(m, field, rectype)
let ae = if accessExpr != nil: "$1.$2" % [accessExpr, sname] let ae = if accessExpr != nil: "$1.$2" % [accessExpr, sname]
else: 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 # 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 # have to recurse via 'getTypeDescAux'. And not doing so prevents problems
# with heavily templatized C++ code: # with heavily templatized C++ code:
if not isImportedCppType(rectype): 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: if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags:
addf(result, "$1 $2[SEQ_DECL_SIZE];$n", addf(result, "$1 $2[SEQ_DECL_SIZE];$n",
[getTypeDescAux(m, fieldType.elemType, check), sname]) [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. # we need to use a weak dependency here for trecursive_table.
addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname]) addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname])
elif field.bitsize != 0: elif field.bitsize != 0:
@ -624,7 +638,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
excl(check, t.id) excl(check, t.id)
return return
case t.kind case t.kind
of tyRef, tyPtr, tyVar: of tyRef, tyOptAsRef, tyPtr, tyVar:
var star = if t.kind == tyVar and tfVarIsPtr notin origTyp.flags and var star = if t.kind == tyVar and tfVarIsPtr notin origTyp.flags and
compileToCpp(m): "&" else: "*" compileToCpp(m): "&" else: "*"
var et = origTyp.skipTypes(abstractInst).lastSon var et = origTyp.skipTypes(abstractInst).lastSon
@ -651,6 +665,21 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
result = name & "*" & star result = name & "*" & star
m.typeCache[sig] = result m.typeCache[sig] = result
pushType(m, et) 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:
# else we have a strong dependency :-( # else we have a strong dependency :-(
result = getTypeDescAux(m, et, check) & star result = getTypeDescAux(m, et, check) & star
@ -726,6 +755,38 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
else: else:
result = rope("TGenericSeq") result = rope("TGenericSeq")
add(result, "*") 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: of tyArray:
var n: BiggestInt = lengthOrd(t) var n: BiggestInt = lengthOrd(t)
if n <= 0: n = 1 # make an array of at least one element 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: elif prc.typ.callConv == ccInline:
result.add "static " result.add "static "
var check = initIntSet() 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) genProcParams(m, prc.typ, rettype, params, check)
# careful here! don't access ``prc.ast`` as that could reload large parts of # careful here! don't access ``prc.ast`` as that could reload large parts of
# the object graph! # the object graph!
@ -1113,6 +1174,8 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
proc genTypeInfo(m: BModule, t: PType): Rope = proc genTypeInfo(m: BModule, t: PType): Rope =
let origType = t let origType = t
var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses) var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses)
if t.kind == tyOpt:
return genTypeInfo(m, optLowering(t))
let sig = hashType(origType) let sig = hashType(origType)
result = m.typeInfoMarker.getOrDefault(sig) result = m.typeInfoMarker.getOrDefault(sig)
@ -1158,7 +1221,7 @@ proc genTypeInfo(m: BModule, t: PType): Rope =
else: else:
let x = fakeClosureType(t.owner) let x = fakeClosureType(t.owner)
genTupleInfo(m, x, x, result) genTupleInfo(m, x, x, result)
of tySequence, tyRef: of tySequence, tyRef, tyOptAsRef:
genTypeInfoAux(m, t, t, result) genTypeInfoAux(m, t, t, result)
if gSelectedGC >= gcMarkAndSweep: if gSelectedGC >= gcMarkAndSweep:
let markerProc = genTraverseProc(m, origType, sig, tiNew) let markerProc = genTraverseProc(m, origType, sig, tiNew)

View file

@ -100,7 +100,7 @@ proc getUniqueType*(key: PType): PType =
if result == nil: if result == nil:
gCanonicalTypes[k] = key gCanonicalTypes[k] = key
result = key result = key
of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor: of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr:
if key.isResolvedUserTypeClass: if key.isResolvedUserTypeClass:
return getUniqueType(lastSon(key)) return getUniqueType(lastSon(key))
if key.sym != nil: if key.sym != nil:
@ -126,7 +126,7 @@ proc getUniqueType*(key: PType): PType =
result = slowSearch(key, k) result = slowSearch(key, k)
of tyGenericInvocation, tyGenericBody, of tyGenericInvocation, tyGenericBody,
tyOpenArray, tyArray, tySet, tyRange, tyTuple, 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 # we have to do a slow linear search because types may need
# to be compared by their structure: # to be compared by their structure:
result = slowSearch(key, k) result = slowSearch(key, k)
@ -157,7 +157,7 @@ proc getUniqueType*(key: PType): PType =
else: else:
# ugh, we need the canon here: # ugh, we need the canon here:
result = slowSearch(key, k) result = slowSearch(key, k)
of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("getUniqueType") of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("getUniqueType")
proc makeSingleLineCString*(s: string): string = proc makeSingleLineCString*(s: string): string =
result = "\"" result = "\""

View file

@ -47,21 +47,31 @@ proc findPendingModule(m: BModule, s: PSym): BModule =
var ms = getModule(s) var ms = getModule(s)
result = m.g.modules[ms.position] 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.k = k
result.s = s result.storage = s
result.t = typ result.lode = lode
result.r = nil result.r = nil
result.flags = {} 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 # fills the loc if it is not already initialized
if a.k == locNone: if a.k == locNone:
a.k = k a.k = k
a.t = typ a.lode = lode
a.s = s a.storage = s
if a.r == nil: a.r = r 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 = proc isSimpleConst(typ: PType): bool =
let t = skipTypes(typ, abstractVar) let t = skipTypes(typ, abstractVar)
result = t.kind notin result = t.kind notin
@ -286,7 +296,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
if not isComplexValueType(typ): if not isComplexValueType(typ):
if containsGcRef: if containsGcRef:
var nilLoc: TLoc var nilLoc: TLoc
initLoc(nilLoc, locTemp, loc.t, OnStack) initLoc(nilLoc, locTemp, loc.lode, OnStack)
nilLoc.r = rope("NIM_NIL") nilLoc.r = rope("NIM_NIL")
genRefAssign(p, loc, nilLoc, {afSrcIsNil}) genRefAssign(p, loc, nilLoc, {afSrcIsNil})
else: else:
@ -294,7 +304,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
else: else:
if optNilCheck in p.options: if optNilCheck in p.options:
linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(loc)) 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", linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
addrLoc(loc), genTypeInfo(p.module, loc.t)) addrLoc(loc), genTypeInfo(p.module, loc.t))
# XXX: generated reset procs should not touch the m_type # 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) & "_" result.r = "T" & rope(p.labels) & "_"
linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r) linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r)
result.k = locTemp result.k = locTemp
result.t = t result.lode = lodeTyp t
result.s = OnStack result.storage = OnStack
result.flags = {} result.flags = {}
constructLoc(p, result, not needsInit) constructLoc(p, result, not needsInit)
@ -350,8 +360,8 @@ proc getIntTemp(p: BProc, result: var TLoc) =
result.r = "T" & rope(p.labels) & "_" result.r = "T" & rope(p.labels) & "_"
linefmt(p, cpsLocals, "NI $1;$n", result.r) linefmt(p, cpsLocals, "NI $1;$n", result.r)
result.k = locTemp result.k = locTemp
result.s = OnStack result.storage = OnStack
result.t = getSysType(tyInt) result.lode = lodeTyp getSysType(tyInt)
result.flags = {} result.flags = {}
proc initGCFrame(p: BProc): Rope = proc initGCFrame(p: BProc): Rope =
@ -375,9 +385,10 @@ proc localDebugInfo(p: BProc, s: PSym) =
inc(p.maxFrameLen) inc(p.maxFrameLen)
inc p.blocks[p.blocks.len-1].frameLen 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: 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) if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
result = getTypeDesc(p.module, s.typ) result = getTypeDesc(p.module, s.typ)
if s.constraint.isNil: if s.constraint.isNil:
@ -390,23 +401,24 @@ proc localVarDecl(p: BProc; s: PSym): Rope =
else: else:
result = s.cgDeclFrmt % [result, s.loc.r] 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 #assert(s.loc.k == locNone) # not yet assigned
# this need not be fulfilled for inline procs; they are regenerated # this need not be fulfilled for inline procs; they are regenerated
# for each module that uses them! # for each module that uses them!
let nl = if optLineDir in gOptions: "" else: tnl let nl = if optLineDir in gOptions: "" else: tnl
let decl = localVarDecl(p, s) & ";" & nl let decl = localVarDecl(p, n) & ";" & nl
line(p, cpsLocals, decl) line(p, cpsLocals, decl)
localDebugInfo(p, s) localDebugInfo(p, n.sym)
include ccgthreadvars include ccgthreadvars
proc varInDynamicLib(m: BModule, sym: PSym) proc varInDynamicLib(m: BModule, sym: PSym)
proc mangleDynLibProc(sym: PSym): Rope 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: 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: if lfDynamicLib in s.loc.flags:
var q = findPendingModule(p.module, s) var q = findPendingModule(p.module, s)
@ -446,9 +458,10 @@ proc assignParam(p: BProc, s: PSym) =
scopeMangledParam(p, s) scopeMangledParam(p, s)
localDebugInfo(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: 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 = proc getLabel(p: BProc): TLabel =
inc(p.labels) inc(p.labels)
@ -457,7 +470,7 @@ proc getLabel(p: BProc): TLabel =
proc fixLabel(p: BProc, labl: TLabel) = proc fixLabel(p: BProc, labl: TLabel) =
lineF(p, cpsStmts, "$1: ;$n", [labl]) 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 requestConstImpl(p: BProc, sym: PSym)
proc genStmts(p: BProc, t: PNode) proc genStmts(p: BProc, t: PNode)
proc expr(p: BProc, n: PNode, d: var TLoc) 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 genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope
proc initLocExpr(p: BProc, e: PNode, result: var TLoc) = proc initLocExpr(p: BProc, e: PNode, result: var TLoc) =
initLoc(result, locNone, e.typ, OnUnknown) initLoc(result, locNone, e, OnUnknown)
expr(p, e, result) expr(p, e, result)
proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) = proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) =
initLoc(result, locNone, e.typ, OnUnknown) initLoc(result, locNone, e, OnUnknown)
result.flags.incl lfSingleUse result.flags.incl lfSingleUse
expr(p, e, result) expr(p, e, result)
@ -590,8 +603,8 @@ proc cgsym(m: BModule, name: string): Rope =
var sym = magicsys.getCompilerProc(name) var sym = magicsys.getCompilerProc(name)
if sym != nil: if sym != nil:
case sym.kind case sym.kind
of skProc, skMethod, skConverter, skIterator: genProc(m, sym) of skProc, skFunc, skMethod, skConverter, skIterator: genProc(m, sym)
of skVar, skResult, skLet: genVarPrototype(m, sym) of skVar, skResult, skLet: genVarPrototype(m, newSymNode sym)
of skType: discard getTypeDesc(m, sym.typ) of skType: discard getTypeDesc(m, sym.typ)
else: internalError("cgsym: " & name & ": " & $sym.kind) else: internalError("cgsym: " & name & ": " & $sym.kind)
else: else:
@ -645,7 +658,7 @@ proc closureSetup(p: BProc, prc: PSym) =
internalError(prc.info, "closure generation failed") internalError(prc.info, "closure generation failed")
var env = ls.sym var env = ls.sym
#echo "created environment: ", env.id, " for ", prc.name.s #echo "created environment: ", env.id, " for ", prc.name.s
assignLocalVar(p, env) assignLocalVar(p, ls)
# generate cast assignment: # generate cast assignment:
linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n", linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n",
rdLoc(env.loc), getTypeDesc(p.module, env.typ)) 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 sfPure notin prc.flags and prc.typ.sons[0] != nil:
if resultPos >= prc.ast.len: if resultPos >= prc.ast.len:
internalError(prc.info, "proc has no result symbol") 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 not isInvalidReturnType(prc.typ.sons[0]):
if sfNoInit in prc.flags: incl(res.flags, sfNoInit) 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): 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 var a: TLoc
initLocExprSingleUse(p, val, a) initLocExprSingleUse(p, val, a)
linefmt(p, cpsStmts, "$1 = $2;$n", decl, rdLoc(a)) linefmt(p, cpsStmts, "$1 = $2;$n", decl, rdLoc(a))
else: else:
# declare the result symbol: # declare the result symbol:
assignLocalVar(p, res) assignLocalVar(p, resNode)
assert(res.loc.r != nil) assert(res.loc.r != nil)
initLocalVar(p, res, immediateAsgn=false) initLocalVar(p, res, immediateAsgn=false)
returnStmt = rfmt(nil, "\treturn $1;$n", rdLoc(res.loc)) returnStmt = rfmt(nil, "\treturn $1;$n", rdLoc(res.loc))
else: else:
fillResult(res) fillResult(resNode)
assignParam(p, res) assignParam(p, res)
if skipTypes(res.typ, abstractInst).kind == tyArray: if skipTypes(res.typ, abstractInst).kind == tyArray:
#incl(res.loc.flags, lfIndirect) #incl(res.loc.flags, lfIndirect)
res.loc.s = OnUnknown res.loc.storage = OnUnknown
for i in countup(1, sonsLen(prc.typ.n) - 1): 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 if param.typ.isCompileTimeOnly: continue
assignParam(p, param) assignParam(p, param)
closureSetup(p, prc) closureSetup(p, prc)
@ -764,13 +778,13 @@ proc genProcPrototype(m: BModule, sym: PSym) =
proc genProcNoForward(m: BModule, prc: PSym) = proc genProcNoForward(m: BModule, prc: PSym) =
if lfImportCompilerProc in prc.loc.flags: if lfImportCompilerProc in prc.loc.flags:
fillProcLoc(m, prc) fillProcLoc(m, prc.ast[namePos])
useHeader(m, prc) useHeader(m, prc)
# dependency to a compilerproc: # dependency to a compilerproc:
discard cgsym(m, prc.name.s) discard cgsym(m, prc.name.s)
return return
if lfNoDecl in prc.loc.flags: if lfNoDecl in prc.loc.flags:
fillProcLoc(m, prc) fillProcLoc(m, prc.ast[namePos])
useHeader(m, prc) useHeader(m, prc)
genProcPrototype(m, prc) genProcPrototype(m, prc)
elif prc.typ.callConv == ccInline: elif prc.typ.callConv == ccInline:
@ -779,7 +793,7 @@ proc genProcNoForward(m: BModule, prc: PSym) =
# a check for ``m.declaredThings``. # a check for ``m.declaredThings``.
if not containsOrIncl(m.declaredThings, prc.id): if not containsOrIncl(m.declaredThings, prc.id):
#if prc.loc.k == locNone: #if prc.loc.k == locNone:
fillProcLoc(m, prc) fillProcLoc(m, prc.ast[namePos])
#elif {sfExportc, sfImportc} * prc.flags == {}: #elif {sfExportc, sfImportc} * prc.flags == {}:
# # reset name to restore consistency in case of hashing collisions: # # reset name to restore consistency in case of hashing collisions:
# echo "resetting ", prc.id, " by ", m.module.name.s # echo "resetting ", prc.id, " by ", m.module.name.s
@ -790,7 +804,7 @@ proc genProcNoForward(m: BModule, prc: PSym) =
genProcAux(m, prc) genProcAux(m, prc)
elif lfDynamicLib in prc.loc.flags: elif lfDynamicLib in prc.loc.flags:
var q = findPendingModule(m, prc) var q = findPendingModule(m, prc)
fillProcLoc(q, prc) fillProcLoc(q, prc.ast[namePos])
useHeader(m, prc) useHeader(m, prc)
genProcPrototype(m, prc) genProcPrototype(m, prc)
if q != nil and not containsOrIncl(q.declaredThings, prc.id): if q != nil and not containsOrIncl(q.declaredThings, prc.id):
@ -799,13 +813,13 @@ proc genProcNoForward(m: BModule, prc: PSym) =
symInDynamicLibPartial(m, prc) symInDynamicLibPartial(m, prc)
elif sfImportc notin prc.flags: elif sfImportc notin prc.flags:
var q = findPendingModule(m, prc) var q = findPendingModule(m, prc)
fillProcLoc(q, prc) fillProcLoc(q, prc.ast[namePos])
useHeader(m, prc) useHeader(m, prc)
genProcPrototype(m, prc) genProcPrototype(m, prc)
if q != nil and not containsOrIncl(q.declaredThings, prc.id): if q != nil and not containsOrIncl(q.declaredThings, prc.id):
genProcAux(q, prc) genProcAux(q, prc)
else: else:
fillProcLoc(m, prc) fillProcLoc(m, prc.ast[namePos])
useHeader(m, prc) useHeader(m, prc)
if sfInfixCall notin prc.flags: genProcPrototype(m, prc) if sfInfixCall notin prc.flags: genProcPrototype(m, prc)
@ -813,7 +827,7 @@ proc requestConstImpl(p: BProc, sym: PSym) =
var m = p.module var m = p.module
useHeader(m, sym) useHeader(m, sym)
if sym.loc.k == locNone: 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 if lfNoDecl in sym.loc.flags: return
# declare implementation: # declare implementation:
var q = findPendingModule(m, sym) 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 sfBorrow in prc.flags or not isActivated(prc): return
if sfForward in prc.flags: if sfForward in prc.flags:
addForwardedProc(m, prc) addForwardedProc(m, prc)
fillProcLoc(m, prc) fillProcLoc(m, prc.ast[namePos])
else: else:
genProcNoForward(m, prc) genProcNoForward(m, prc)
if {sfExportc, sfCompilerProc} * prc.flags == {sfExportc} and 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): if not containsOrIncl(m.g.generatedHeader.declaredThings, prc.id):
genProcAux(m.g.generatedHeader, prc) genProcAux(m.g.generatedHeader, prc)
proc genVarPrototypeAux(m: BModule, sym: PSym) = proc genVarPrototypeAux(m: BModule, n: PNode) =
#assert(sfGlobal in sym.flags) #assert(sfGlobal in sym.flags)
let sym = n.sym
useHeader(m, 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): if (lfNoDecl in sym.loc.flags) or containsOrIncl(m.declaredThings, sym.id):
return return
if sym.owner.id != m.module.id: 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") if sfVolatile in sym.flags: add(m.s[cfsVars], " volatile")
addf(m.s[cfsVars], " $1;$n", [sym.loc.r]) addf(m.s[cfsVars], " $1;$n", [sym.loc.r])
proc genVarPrototype(m: BModule, sym: PSym) = proc genVarPrototype(m: BModule, n: PNode) =
genVarPrototypeAux(m, sym) genVarPrototypeAux(m, n)
proc addIntTypes(result: var Rope) {.inline.} = proc addIntTypes(result: var Rope) {.inline.} =
addf(result, "#define NIM_NEW_MANGLING_RULES" & tnl & addf(result, "#define NIM_NEW_MANGLING_RULES" & tnl &

View file

@ -107,3 +107,4 @@ proc initDefines*() =
defineSymbol("nimDistros") defineSymbol("nimDistros")
defineSymbol("nimHasCppDefine") defineSymbol("nimHasCppDefine")
defineSymbol("nimGenericInOutFlags") 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``. ## section of ``doc/docgen.txt``.
result = baseName result = baseName
case k: case k:
of skProc: result.add(defaultParamSeparator) of skProc, skFunc: result.add(defaultParamSeparator)
of skMacro: result.add(".m" & defaultParamSeparator) of skMacro: result.add(".m" & defaultParamSeparator)
of skMethod: result.add(".e" & defaultParamSeparator) of skMethod: result.add(".e" & defaultParamSeparator)
of skIterator: result.add(".i" & defaultParamSeparator) of skIterator: result.add(".i" & defaultParamSeparator)
@ -341,7 +341,7 @@ proc isCallable(n: PNode): bool =
## Returns true if `n` contains a callable node. ## Returns true if `n` contains a callable node.
case n.kind case n.kind
of nkProcDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, of nkProcDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef,
nkConverterDef: result = true nkConverterDef, nkFuncDef: result = true
else: else:
result = false result = false
@ -533,6 +533,9 @@ proc generateDoc*(d: PDoc, n: PNode) =
of nkProcDef: of nkProcDef:
when useEffectSystem: documentRaises(n) when useEffectSystem: documentRaises(n)
genItem(d, n, n.sons[namePos], skProc) genItem(d, n, n.sons[namePos], skProc)
of nkFuncDef:
when useEffectSystem: documentRaises(n)
genItem(d, n, n.sons[namePos], skFunc)
of nkMethodDef: of nkMethodDef:
when useEffectSystem: documentRaises(n) when useEffectSystem: documentRaises(n)
genItem(d, n, n.sons[namePos], skMethod) genItem(d, n, n.sons[namePos], skMethod)
@ -574,6 +577,9 @@ proc generateJson*(d: PDoc, n: PNode) =
of nkProcDef: of nkProcDef:
when useEffectSystem: documentRaises(n) when useEffectSystem: documentRaises(n)
d.add genJsonItem(d, n, n.sons[namePos], skProc) 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: of nkMethodDef:
when useEffectSystem: documentRaises(n) when useEffectSystem: documentRaises(n)
d.add genJsonItem(d, n, n.sons[namePos], skMethod) 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) = proc genSection(d: PDoc, kind: TSymKind) =
const sectionNames: array[skModule..skTemplate, string] = [ const sectionNames: array[skModule..skTemplate, string] = [
"Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods", "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Funcs",
"Iterators", "Converters", "Macros", "Templates" "Methods", "Iterators", "Converters", "Macros", "Templates"
] ]
if d.section[kind] == nil: return if d.section[kind] == nil: return
var title = sectionNames[kind].rope var title = sectionNames[kind].rope

View file

@ -652,9 +652,10 @@ proc getLinkCmd(projectfile, objfiles: string): string =
else: else:
var linkerExe = getConfigVar(cCompiler, ".linkerexe") var linkerExe = getConfigVar(cCompiler, ".linkerexe")
if len(linkerExe) == 0: linkerExe = cCompiler.getLinkerExe if len(linkerExe) == 0: linkerExe = cCompiler.getLinkerExe
# bug #6452: We must not use ``quoteShell`` here for ``linkerExe``
if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe") if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe")
if noAbsolutePaths(): result = quoteShell(linkerExe) if noAbsolutePaths(): result = linkerExe
else: result = quoteShell(joinPath(ccompilerpath, linkerExe)) else: result = joinPath(ccompilerpath, linkerExe)
let buildgui = if optGenGuiApp in gGlobalOptions: CC[cCompiler].buildGui let buildgui = if optGenGuiApp in gGlobalOptions: CC[cCompiler].buildGui
else: "" else: ""
var exefile, builddll: string var exefile, builddll: string
@ -701,6 +702,40 @@ template tryExceptOSErrorMessage(errorPrefix: string = "", body: untyped): typed
rawMessage(errExecutionOfProgramFailed, ose.msg & " " & $ose.errorCode) rawMessage(errExecutionOfProgramFailed, ose.msg & " " & $ose.errorCode)
raise 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) = proc callCCompiler*(projectfile: string) =
var var
linkCmd: string linkCmd: string
@ -713,35 +748,9 @@ proc callCCompiler*(projectfile: string) =
var prettyCmds: TStringSeq = @[] var prettyCmds: TStringSeq = @[]
let prettyCb = proc (idx: int) = let prettyCb = proc (idx: int) =
echo prettyCmds[idx] 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) compileCFile(toCompile, script, cmds, prettyCmds)
if optCompileOnly notin gGlobalOptions: if optCompileOnly notin gGlobalOptions:
if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors() execCmdsInParallel(cmds, prettyCb)
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())
if optNoLinking notin gGlobalOptions: if optNoLinking notin gGlobalOptions:
# call the linker: # call the linker:
var objfiles = "" var objfiles = ""
@ -756,9 +765,7 @@ proc callCCompiler*(projectfile: string) =
linkCmd = getLinkCmd(projectfile, objfiles) linkCmd = getLinkCmd(projectfile, objfiles)
if optCompileOnly notin gGlobalOptions: if optCompileOnly notin gGlobalOptions:
tryExceptOSErrorMessage("invocation of external linker program failed."): execLinkCmd(linkCmd)
execExternalProgram(linkCmd,
if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking)
else: else:
linkCmd = "" linkCmd = ""
if optGenScript in gGlobalOptions: if optGenScript in gGlobalOptions:
@ -766,7 +773,8 @@ proc callCCompiler*(projectfile: string) =
add(script, tnl) add(script, tnl)
generateScript(projectfile, script) generateScript(projectfile, script)
from json import escapeJson #from json import escapeJson
import json
proc writeJsonBuildInstructions*(projectfile: string) = proc writeJsonBuildInstructions*(projectfile: string) =
template lit(x: untyped) = f.write x template lit(x: untyped) = f.write x
@ -834,6 +842,34 @@ proc writeJsonBuildInstructions*(projectfile: string) =
lit "\L}\L" lit "\L}\L"
close(f) 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 = proc genMappingFiles(list: CFileList): Rope =
for it in list: for it in list:
addf(result, "--file:r\"$1\"$N", [rope(it.cname)]) 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 import
intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups, 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 evalImport*(c: PContext, n: PNode): PNode
proc evalFrom*(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 = proc getModuleName*(n: PNode): string =
# This returns a short relative module name without the nim extension # This returns a short relative module name without the nim extension
# e.g. like "system", "importer" or "somepath/module" # e.g. like "system", "importer" or "somepath/module"
@ -31,16 +42,33 @@ proc getModuleName*(n: PNode): string =
result = n.ident.s result = n.ident.s
of nkSym: of nkSym:
result = n.sym.name.s result = n.sym.name.s
of nkInfix, nkPrefix: of nkInfix:
if n.sons[0].kind == nkIdent and n.sons[0].ident.id == getIdent("as").id: let n0 = n[0]
let n1 = n[1]
if n0.kind == nkIdent and n0.ident.id == getIdent("as").id:
# XXX hack ahead: # XXX hack ahead:
n.kind = nkImportAs n.kind = nkImportAs
n.sons[0] = n.sons[1] n.sons[0] = n.sons[1]
n.sons[1] = n.sons[2] n.sons[1] = n.sons[2]
n.sons.setLen(2) n.sons.setLen(2)
return getModuleName(n.sons[0]) return getModuleName(n.sons[0])
# hacky way to implement 'x / y /../ z': if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$":
result = renderTree(n, {renderNoComments}).replace(" ") 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: of nkDotExpr:
result = renderTree(n, {renderNoComments}).replace(".", "/") result = renderTree(n, {renderNoComments}).replace(".", "/")
of nkImportAs: of nkImportAs:
@ -60,6 +88,11 @@ proc checkModuleName*(n: PNode; doLocalError=true): int32 =
else: else:
result = fullPath.fileInfoIdx 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) = proc rawImportSymbol(c: PContext, s: PSym) =
# This does not handle stubs, because otherwise loading on demand would be # This does not handle stubs, because otherwise loading on demand would be
# pointless in practice. So importing stubs is fine here! # pointless in practice. So importing stubs is fine here!
@ -75,7 +108,7 @@ proc rawImportSymbol(c: PContext, s: PSym) =
strTableAdd(c.importTable.symbols, s) strTableAdd(c.importTable.symbols, s)
if s.kind == skType: if s.kind == skType:
var etyp = s.typ 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): for j in countup(0, sonsLen(etyp.n) - 1):
var e = etyp.n.sons[j].sym var e = etyp.n.sons[j].sym
if e.kind != skEnumField: if e.kind != skEnumField:
@ -91,7 +124,10 @@ proc rawImportSymbol(c: PContext, s: PSym) =
break break
check = nextIdentIter(it, c.importTable.symbols) check = nextIdentIter(it, c.importTable.symbols)
if e != nil: if e != nil:
rawImportSymbol(c, e) if sfPure notin s.flags:
rawImportSymbol(c, e)
else:
importPureEnumField(c, e)
else: else:
# rodgen assures that converters and patterns are no stubs # rodgen assures that converters and patterns are no stubs
if s.kind == skConverter: addConverter(c, s) 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): for i in countup(0, sonsLen(n) - 1):
let it = n.sons[i] let it = n.sons[i]
if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket: if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
let sep = renderTree(it.sons[0], {renderNoComments}) let sep = it[0]
let dir = renderTree(it.sons[1], {renderNoComments}) 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]: for x in it[2]:
let f = renderTree(x, {renderNoComments}) a.sons[2] = x
let a = newStrNode(nkStrLit, (dir & sep & f).replace(" "))
a.info = it.info
impMod(c, a) impMod(c, a)
else: else:
impMod(c, it) impMod(c, it)

View file

@ -187,12 +187,12 @@ proc mapType(typ: PType): TJSTypeKind =
of tyBool: result = etyBool of tyBool: result = etyBool
of tyFloat..tyFloat128: result = etyFloat of tyFloat..tyFloat128: result = etyFloat
of tySet: result = etyObject # map a set to a table 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: of tyObject, tyArray, tyTuple, tyOpenArray, tyVarargs:
result = etyObject result = etyObject
of tyNil: result = etyNull of tyNil: result = etyNull
of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvocation, of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvocation,
tyNone, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor, tyNone, tyFromExpr, tyForward, tyEmpty,
tyExpr, tyStmt, tyTypeDesc, tyTypeClasses, tyVoid, tyAlias: tyExpr, tyStmt, tyTypeDesc, tyTypeClasses, tyVoid, tyAlias:
result = etyNone result = etyNone
of tyInferred: of tyInferred:
@ -202,7 +202,7 @@ proc mapType(typ: PType): TJSTypeKind =
else: result = etyNone else: result = etyNone
of tyProc: result = etyProc of tyProc: result = etyProc
of tyCString: result = etyString 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 = proc mapType(p: PProc; typ: PType): TJSTypeKind =
if p.target == targetPHP: result = etyObject if p.target == targetPHP: result = etyObject
@ -292,7 +292,7 @@ proc useMagic(p: PProc, name: string) =
if name.len == 0: return if name.len == 0: return
var s = magicsys.getCompilerProc(name) var s = magicsys.getCompilerProc(name)
if s != nil: 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): if not p.g.generatedSyms.containsOrIncl(s.id):
let code = genProc(p, s) let code = genProc(p, s)
add(p.g.constants, code) 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 # we don't care if it's an etyBaseIndex (global) of a string, it's
# still a string that needs to be copied properly: # 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 xtyp = etySeq
case xtyp case xtyp
of etySeq: 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 # 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 # here for 'shallowCopy'. This is an educated guess and might require further
# changes later: # 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) genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=noCopy)
proc genSwap(p: PProc, n: PNode) = proc genSwap(p: PProc, n: PNode) =
@ -1111,7 +1111,7 @@ template isIndirect(x: PSym): bool =
({sfAddrTaken, sfGlobal} * v.flags != {} and ({sfAddrTaken, sfGlobal} * v.flags != {} and
#(mapType(v.typ) != etyObject) and #(mapType(v.typ) != etyObject) and
{sfImportc, sfVolatile, sfExportc} * v.flags == {} 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) skConst, skTemp, skLet} and p.target == targetJS)
proc genAddr(p: PProc, n: PNode, r: var TCompRes) = proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
@ -1237,7 +1237,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
else: else:
r.res = "$" & s.loc.r r.res = "$" & s.loc.r
p.declareGlobal(s.id, r.res) p.declareGlobal(s.id, r.res)
of skProc, skConverter, skMethod: of skProc, skFunc, skConverter, skMethod:
discard mangleName(s, p.target) discard mangleName(s, p.target)
if p.target == targetPHP and r.kind != resCallee: if p.target == targetPHP and r.kind != resCallee:
r.res = makeJsString($s.loc.r) r.res = makeJsString($s.loc.r)
@ -1550,7 +1550,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
result = putToSeq("[null, 0]", indirect) result = putToSeq("[null, 0]", indirect)
else: else:
result = putToSeq("null", indirect) result = putToSeq("null", indirect)
of tySequence, tyString, tyCString, tyPointer, tyProc: of tySequence, tyOpt, tyString, tyCString, tyPointer, tyProc:
result = putToSeq("null", indirect) result = putToSeq("null", indirect)
of tyStatic: of tyStatic:
if t.n != nil: if t.n != nil:
@ -2338,7 +2338,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
nkFromStmt, nkTemplateDef, nkMacroDef: discard nkFromStmt, nkTemplateDef, nkMacroDef: discard
of nkPragma: genPragma(p, n) of nkPragma: genPragma(p, n)
of nkProcDef, nkMethodDef, nkConverterDef: of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef:
var s = n.sons[namePos].sym var s = n.sons[namePos].sym
if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}: if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}:
genSym(p, n.sons[namePos], r) genSym(p, n.sons[namePos], r)

View file

@ -194,7 +194,7 @@ proc illegalCapture(s: PSym): bool {.inline.} =
s.kind == skResult s.kind == skResult
proc isInnerProc(s: PSym): bool = 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 result = s.skipGenericOwner.kind in routineKinds
proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode = proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
@ -371,7 +371,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
case n.kind case n.kind
of nkSym: of nkSym:
let s = n.sym 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 # this handles the case that the inner proc was declared as
# .closure but does not actually capture anything: # .closure but does not actually capture anything:
addClosureParam(c, s, n.info) addClosureParam(c, s, n.info)
@ -443,7 +444,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
discard discard
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef: of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef:
discard discard
of nkLambdaKinds, nkIteratorDef: of nkLambdaKinds, nkIteratorDef, nkFuncDef:
if n.typ != nil: if n.typ != nil:
detectCapturedVars(n[namePos], owner, c) detectCapturedVars(n[namePos], owner, c)
else: else:
@ -730,7 +731,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
# now we know better, so patch it: # now we know better, so patch it:
n.sons[0] = x.sons[0] n.sons[0] = x.sons[0]
n.sons[1] = x.sons[1] n.sons[1] = x.sons[1]
of nkLambdaKinds, nkIteratorDef: of nkLambdaKinds, nkIteratorDef, nkFuncDef:
if n.typ != nil and n[namePos].kind == nkSym: if n.typ != nil and n[namePos].kind == nkSym:
let m = newSymNode(n[namePos].sym) let m = newSymNode(n[namePos].sym)
m.typ = n.typ m.typ = n.typ

View file

@ -48,7 +48,7 @@ type
tkShl, tkShr, tkStatic, tkShl, tkShr, tkStatic,
tkTemplate, tkTemplate,
tkTry, tkTuple, tkType, tkUsing, tkTry, tkTuple, tkType, tkUsing,
tkVar, tkWhen, tkWhile, tkWith, tkWithout, tkXor, tkVar, tkWhen, tkWhile, tkXor,
tkYield, # end of keywords tkYield, # end of keywords
tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit, tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit,
tkUIntLit, tkUInt8Lit, tkUInt16Lit, tkUInt32Lit, tkUInt64Lit, tkUIntLit, tkUInt8Lit, tkUInt16Lit, tkUInt32Lit, tkUInt64Lit,
@ -89,7 +89,7 @@ const
"shl", "shr", "static", "shl", "shr", "static",
"template", "template",
"try", "tuple", "type", "using", "try", "tuple", "type", "using",
"var", "when", "while", "with", "without", "xor", "var", "when", "while", "xor",
"yield", "yield",
"tkIntLit", "tkInt8Lit", "tkInt16Lit", "tkInt32Lit", "tkInt64Lit", "tkIntLit", "tkInt8Lit", "tkInt16Lit", "tkInt32Lit", "tkInt64Lit",
"tkUIntLit", "tkUInt8Lit", "tkUInt16Lit", "tkUInt32Lit", "tkUInt64Lit", "tkUIntLit", "tkUInt8Lit", "tkUInt16Lit", "tkUInt32Lit", "tkUInt64Lit",

View file

@ -144,8 +144,10 @@ type
proc getSymRepr*(s: PSym): string = proc getSymRepr*(s: PSym): string =
case s.kind case s.kind
of skProc, skMethod, skConverter, skIterator: result = getProcHeader(s) of skProc, skFunc, skMethod, skConverter, skIterator:
else: result = s.name.s result = getProcHeader(s)
else:
result = s.name.s
proc ensureNoMissingOrUnusedSymbols(scope: PScope) = proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
# check if all symbols have been used and defined: # check if all symbols have been used and defined:
@ -286,7 +288,7 @@ proc lookUp*(c: PContext, n: PNode): PSym =
type type
TLookupFlag* = enum TLookupFlag* = enum
checkAmbiguity, checkUndeclared, checkModule checkAmbiguity, checkUndeclared, checkModule, checkPureEnumFields
proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
const allExceptModule = {low(TSymKind)..high(TSymKind)}-{skModule,skPackage} 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) result = searchInScopes(c, ident).skipAlias(n)
else: else:
result = searchInScopes(c, ident, allExceptModule).skipAlias(n) 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: if result == nil and checkUndeclared in flags:
fixSpelling(n, ident, searchInScopes) fixSpelling(n, ident, searchInScopes)
errorUndeclaredIdentifier(c, n.info, ident.s) errorUndeclaredIdentifier(c, n.info, ident.s)

View file

@ -633,7 +633,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
if fn.kind == nkClosure: if fn.kind == nkClosure:
localError(n.info, "closure in spawn environment is not allowed") localError(n.info, "closure in spawn environment is not allowed")
if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro, 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 # for indirect calls we pass the function pointer in the scratchObj
var argType = n[0].typ.skipTypes(abstractInst) var argType = n[0].typ.skipTypes(abstractInst)
var field = newSym(skField, getIdent"fn", owner, n.info) 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.callCCompiler(proj)
extccomp.writeJsonBuildInstructions(proj) extccomp.writeJsonBuildInstructions(proj)
proc commandJsonScript(graph: ModuleGraph; cache: IdentCache) =
let proj = changeFileExt(gProjectFull, "")
extccomp.runJsonBuildInstructions(proj)
proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) = proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) =
#incl(gGlobalOptions, optSafeCode) #incl(gGlobalOptions, optSafeCode)
setTarget(osJS, cpuJS) setTarget(osJS, cpuJS)
@ -266,6 +270,9 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
of "nop", "help": of "nop", "help":
# prevent the "success" message: # prevent the "success" message:
gCmd = cmdDump gCmd = cmdDump
of "jsonscript":
gCmd = cmdJsonScript
commandJsonScript(graph, cache)
else: else:
rawMessage(errInvalidCommandX, command) rawMessage(errInvalidCommandX, command)

View file

@ -119,4 +119,6 @@ condsyms.initDefines()
when not defined(selftest): when not defined(selftest):
handleCmdLine(newIdentCache(), newConfigRef()) handleCmdLine(newIdentCache(), newConfigRef())
when declared(GC_setMaxPause):
echo GC_getStatistics()
msgQuit(int8(msgs.gErrorCounter > 0)) msgQuit(int8(msgs.gErrorCounter > 0))

View file

@ -16,11 +16,11 @@ proc addPath*(path: string, info: TLineInfo) =
options.searchPaths.insert(path, 0) options.searchPaths.insert(path, 0)
type 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, doAssert(ver.len == 0 or ver[0] in {'#', '\0'} + Digits,
"Wrong version: " & ver) "Wrong version: " & ver)
return Version(ver) return Version(ver)
@ -28,7 +28,7 @@ proc newVersion(ver: string): Version =
proc isSpecial(ver: Version): bool = proc isSpecial(ver: Version): bool =
return ($ver).len > 0 and ($ver)[0] == '#' return ($ver).len > 0 and ($ver)[0] == '#'
proc `<`(ver: Version, ver2: Version): bool = proc `<`*(ver: Version, ver2: Version): bool =
## This is synced from Nimble's version module. ## This is synced from Nimble's version module.
# Handling for special versions such as "#head" or "#branch". # Handling for special versions such as "#head" or "#branch".

View file

@ -91,7 +91,8 @@ type
cmdRst2html, # convert a reStructuredText file to HTML cmdRst2html, # convert a reStructuredText file to HTML
cmdRst2tex, # convert a reStructuredText file to TeX cmdRst2tex, # convert a reStructuredText file to TeX
cmdInteractive, # start interactive session 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] TStringSeq* = seq[string]
TGCMode* = enum # the selected GC TGCMode* = enum # the selected GC
gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcRefc, gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcRefc,

View file

@ -255,13 +255,6 @@ proc isUnary(p: TParser): bool =
p.tok.strongSpaceB == 0 and p.tok.strongSpaceB == 0 and
p.tok.strongSpaceA > 0: p.tok.strongSpaceA > 0:
result = true 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.} = proc checkBinary(p: TParser) {.inline.} =
## Check if the current parser token is a binary operator. ## 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 result = r
template somePar() = template somePar() =
if p.tok.strongSpaceA > 0: if p.tok.strongSpaceA > 0: break
if p.strongSpaces:
break
else:
parMessage(p, warnDeprecated,
"a [b] will be parsed as command syntax; spacing")
# progress guaranteed # progress guaranteed
while p.tok.indent < 0 or while p.tok.indent < 0 or
(p.tok.tokType == tkDot and p.tok.indent >= baseIndent): (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: if params.kind != nkEmpty:
result = newProcNode(nkDo, info, result, params = params, pragmas = pragmas) 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)? #| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)?
# either a proc type or a anonymous proc # either a proc type or a anonymous proc
let info = parLineInfo(p) let info = parLineInfo(p)
@ -1002,7 +990,7 @@ proc parseProcExpr(p: var TParser, isExpr: bool): PNode =
if p.tok.tokType == tkEquals and isExpr: if p.tok.tokType == tkEquals and isExpr:
getTok(p) getTok(p)
skipComment(p, result) skipComment(p, result)
result = newProcNode(nkLambda, info, parseStmt(p), result = newProcNode(kind, info, parseStmt(p),
params = params, params = params,
pragmas = pragmas) pragmas = pragmas)
else: else:
@ -1014,7 +1002,7 @@ proc parseProcExpr(p: var TParser, isExpr: bool): PNode =
proc isExprStart(p: TParser): bool = proc isExprStart(p: TParser): bool =
case p.tok.tokType case p.tok.tokType
of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, 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, tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr,
tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut: tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut:
result = true result = true
@ -1038,9 +1026,15 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
optInd(p, result) optInd(p, result)
if not isOperator(p.tok) and isExprStart(p): if not isOperator(p.tok) and isExprStart(p):
addSon(result, primary(p, mode)) addSon(result, primary(p, mode))
if kind == nkDistinctTy and p.tok.tokType in {tkWith, tkWithout}: if kind == nkDistinctTy and p.tok.tokType == tkSymbol:
let nodeKind = if p.tok.tokType == tkWith: nkWith # XXX document this feature!
else: nkWithout var nodeKind: TNodeKind
if p.tok.ident.s == "with":
nodeKind = nkWith
elif p.tok.ident.s == "without":
nodeKind = nkWithout
else:
return result
getTok(p) getTok(p)
let list = newNodeP(nodeKind, p) let list = newNodeP(nodeKind, p)
result.addSon list result.addSon list
@ -1088,21 +1082,12 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
case p.tok.tokType: case p.tok.tokType:
of tkTuple: result = parseTuple(p, mode == pmTypeDef) 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: of tkIterator:
when false: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkLambda)
if mode in {pmTypeDesc, pmTypeDef}: if result.kind == nkLambda: result.kind = nkIteratorDef
result = parseProcExpr(p, false) else: result.kind = nkIteratorTy
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
of tkEnum: of tkEnum:
if mode == pmTypeDef: if mode == pmTypeDef:
result = parseEnum(p) result = parseEnum(p)
@ -2000,6 +1985,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
of tkDefer: result = parseStaticOrDefer(p, nkDefer) of tkDefer: result = parseStaticOrDefer(p, nkDefer)
of tkAsm: result = parseAsm(p) of tkAsm: result = parseAsm(p)
of tkProc: result = parseRoutine(p, nkProcDef) of tkProc: result = parseRoutine(p, nkProcDef)
of tkFunc: result = parseRoutine(p, nkFuncDef)
of tkMethod: result = parseRoutine(p, nkMethodDef) of tkMethod: result = parseRoutine(p, nkMethodDef)
of tkIterator: result = parseRoutine(p, nkIteratorDef) of tkIterator: result = parseRoutine(p, nkIteratorDef)
of tkMacro: result = parseRoutine(p, nkMacroDef) of tkMacro: result = parseRoutine(p, nkMacroDef)
@ -2056,8 +2042,8 @@ proc parseStmt(p: var TParser): PNode =
else: else:
# the case statement is only needed for better error messages: # the case statement is only needed for better error messages:
case p.tok.tokType case p.tok.tokType
of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkIterator, of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkFunc,
tkMacro, tkType, tkConst, tkWhen, tkVar: tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar:
parMessage(p, errComplexStmtRequiresInd) parMessage(p, errComplexStmtRequiresInd)
result = ast.emptyNode result = ast.emptyNode
else: else:

View file

@ -64,7 +64,7 @@ proc astNeeded*(s: PSym): bool =
# needs to be stored. The passes manager frees s.sons[codePos] when # needs to be stored. The passes manager frees s.sons[codePos] when
# appropriate to free the procedure body's memory. This is important # appropriate to free the procedure body's memory. This is important
# to keep memory usage down. # 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 ({sfCompilerProc, sfCompileTime} * s.flags == {}) and
(s.typ.callConv != ccInline) and (s.typ.callConv != ccInline) and
(s.ast.sons[genericParamsPos].kind == nkEmpty): (s.ast.sons[genericParamsPos].kind == nkEmpty):

View file

@ -906,9 +906,14 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
optInd(p, result) optInd(p, result)
if not isOperator(p.tok) and isExprStart(p): if not isOperator(p.tok) and isExprStart(p):
addSon(result, primary(p, mode)) addSon(result, primary(p, mode))
if kind == nkDistinctTy and p.tok.tokType in {tkWith, tkWithout}: if kind == nkDistinctTy and p.tok.tokType == tkSymbol:
let nodeKind = if p.tok.tokType == tkWith: nkWith var nodeKind: TNodeKind
else: nkWithout if p.tok.ident.s == "with":
nodeKind = nkWith
elif p.tok.ident.s == "without":
nodeKind = nkWithout
else:
return result
getTok(p) getTok(p)
let list = newNodeP(nodeKind, p) let list = newNodeP(nodeKind, p)
result.addSon list result.addSon list

View file

@ -1079,9 +1079,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
gsub(g, n.sons[0]) gsub(g, n.sons[0])
if n.len > 1: if n.len > 1:
if n[1].kind == nkWith: if n[1].kind == nkWith:
putWithSpace(g, tkWith, " with") putWithSpace(g, tkSymbol, " with")
else: else:
putWithSpace(g, tkWithout, " without") putWithSpace(g, tkSymbol, " without")
gcomma(g, n[1]) gcomma(g, n[1])
else: else:
put(g, tkDistinct, "distinct") put(g, tkDistinct, "distinct")
@ -1166,6 +1166,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
of nkProcDef: of nkProcDef:
if renderNoProcDefs notin g.flags: putWithSpace(g, tkProc, "proc") if renderNoProcDefs notin g.flags: putWithSpace(g, tkProc, "proc")
gproc(g, n) gproc(g, n)
of nkFuncDef:
if renderNoProcDefs notin g.flags: putWithSpace(g, tkFunc, "func")
gproc(g, n)
of nkConverterDef: of nkConverterDef:
if renderNoProcDefs notin g.flags: putWithSpace(g, tkConverter, "converter") if renderNoProcDefs notin g.flags: putWithSpace(g, tkConverter, "converter")
gproc(g, n) gproc(g, n)
@ -1363,7 +1366,13 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
proc renderTree(n: PNode, renderFlags: TRenderFlags = {}): string = proc renderTree(n: PNode, renderFlags: TRenderFlags = {}): string =
var g: TSrcGen var g: TSrcGen
initSrcGen(g, renderFlags) 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 result = g.buf
proc renderModule(n: PNode, filename: string, 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) loc.k = low(loc.k)
if r.s[r.pos] == '*': if r.s[r.pos] == '*':
inc(r.pos) inc(r.pos)
loc.s = TStorageLoc(decodeVInt(r.s, r.pos)) loc.storage = TStorageLoc(decodeVInt(r.s, r.pos))
else: else:
loc.s = low(loc.s) loc.storage = low(loc.storage)
if r.s[r.pos] == '$': if r.s[r.pos] == '$':
inc(r.pos) inc(r.pos)
loc.flags = cast[TLocFlags](int32(decodeVInt(r.s, 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 = {} loc.flags = {}
if r.s[r.pos] == '^': if r.s[r.pos] == '^':
inc(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: else:
loc.t = nil loc.lode = nil
if r.s[r.pos] == '!': if r.s[r.pos] == '!':
inc(r.pos) inc(r.pos)
loc.r = rope(decodeStr(r.s, r.pos)) loc.r = rope(decodeStr(r.s, r.pos))
@ -588,7 +589,7 @@ proc cmdChangeTriggersRecompilation(old, new: TCommands): bool =
return false return false
of cmdNone, cmdDoc, cmdInterpret, cmdPretty, cmdGenDepend, cmdDump, of cmdNone, cmdDoc, cmdInterpret, cmdPretty, cmdGenDepend, cmdDump,
cmdCheck, cmdParse, cmdScan, cmdIdeTools, cmdDef, cmdCheck, cmdParse, cmdScan, cmdIdeTools, cmdDef,
cmdRst2html, cmdRst2tex, cmdInteractive, cmdRun: cmdRst2html, cmdRst2tex, cmdInteractive, cmdRun, cmdJsonScript:
discard discard
# else: trigger recompilation: # else: trigger recompilation:
result = true result = true

View file

@ -175,16 +175,17 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
var oldLen = result.len var oldLen = result.len
result.add('<') result.add('<')
if loc.k != low(loc.k): encodeVInt(ord(loc.k), result) 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, '*') add(result, '*')
encodeVInt(ord(loc.s), result) encodeVInt(ord(loc.storage), result)
if loc.flags != {}: if loc.flags != {}:
add(result, '$') add(result, '$')
encodeVInt(cast[int32](loc.flags), result) encodeVInt(cast[int32](loc.flags), result)
if loc.t != nil: if loc.lode != nil:
add(result, '^') add(result, '^')
encodeVInt(cast[int32](loc.t.id), result) encodeNode(w, unknownLineInfo(), loc.lode, result)
pushType(w, loc.t) #encodeVInt(cast[int32](loc.t.id), result)
#pushType(w, loc.t)
if loc.r != nil: if loc.r != nil:
add(result, '!') add(result, '!')
encodeStr($loc.r, 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]) for i in countup(0, sonsLen(n) - 1): discard process(c, n.sons[i])
#var s = n.sons[namePos].sym #var s = n.sons[namePos].sym
#addInterfaceSym(w, s) #addInterfaceSym(w, s)
of nkProcDef, nkIteratorDef, nkConverterDef, of nkProcDef, nkFuncDef, nkIteratorDef, nkConverterDef,
nkTemplateDef, nkMacroDef: nkTemplateDef, nkMacroDef:
let s = n.sons[namePos].sym let s = n.sons[namePos].sym
if s == nil: internalError(n.info, "rodwrite.process") 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) = proc addCodeForGenerics(c: PContext, n: PNode) =
for i in countup(c.lastGenericIdx, c.generics.len - 1): for i in countup(c.lastGenericIdx, c.generics.len - 1):
var prc = c.generics[i].inst.sym 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: if prc.ast == nil or prc.ast.sons[bodyPos] == nil:
internalError(prc.info, "no code for " & prc.name.s) internalError(prc.info, "no code for " & prc.name.s)
else: else:
@ -495,13 +495,15 @@ proc isImportSystemStmt(n: PNode): bool =
case n.kind case n.kind
of nkImportStmt: of nkImportStmt:
for x in n: 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: if f == magicsys.systemModule.info.fileIndex:
return true return true
of nkImportExceptStmt, nkFromStmt:
let f = checkModuleName(n[0], false)
if f == magicsys.systemModule.info.fileIndex:
return true
else: discard else: discard
proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = 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 case t.kind
of tyNone, tyEmpty, tyVoid: discard of tyNone, tyEmpty, tyVoid: discard
of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
tyPtr, tyString, tyRef: tyPtr, tyString, tyRef, tyOpt:
defaultOp(c, t, body, x, y) defaultOp(c, t, body, x, y)
of tyArray, tySequence: of tyArray, tySequence:
if tfHasAsgn in t.flags: if tfHasAsgn in t.flags:
@ -227,9 +227,9 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
tyTypeDesc, tyGenericInvocation, tyForward: tyTypeDesc, tyGenericInvocation, tyForward:
internalError(c.info, "assignment requested for type: " & typeToString(t)) internalError(c.info, "assignment requested for type: " & typeToString(t))
of tyOrdinal, tyRange, tyInferred, of tyOrdinal, tyRange, tyInferred,
tyGenericInst, tyFieldAccessor, tyStatic, tyVar, tyAlias: tyGenericInst, tyStatic, tyVar, tyAlias:
liftBodyAux(c, lastSon(t), body, x, y) 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 = proc newProcType(info: TLineInfo; owner: PSym): PType =
result = newType(tyProc, owner) 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): for i in countup(0, len(a)-1):
var candidate = a.sons[i].sym var candidate = a.sons[i].sym
if candidate.kind in {skProc, skMethod, skConverter, if candidate.kind in {skProc, skMethod, skConverter,
skIterator}: skFunc, skIterator}:
# it suffices that the candidate has the proper number of generic # it suffices that the candidate has the proper number of generic
# type parameters: # type parameters:
if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1: if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1:

View file

@ -112,6 +112,7 @@ type
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable, semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable,
info: TLineInfo): PSym info: TLineInfo): PSym
includedFiles*: IntSet # used to detect recursive include files includedFiles*: IntSet # used to detect recursive include files
pureEnumFields*: TStrTable # pure enum fields that can be used unambiguously
userPragmas*: TStrTable userPragmas*: TStrTable
evalContext*: PEvalContext evalContext*: PEvalContext
unknownIdents*: IntSet # ids of all unknown identifiers to prevent 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.converters = @[]
result.patterns = @[] result.patterns = @[]
result.includedFiles = initIntSet() result.includedFiles = initIntSet()
initStrTable(result.pureEnumFields)
initStrTable(result.userPragmas) initStrTable(result.userPragmas)
result.generics = @[] result.generics = @[]
result.unknownIdents = initIntSet() result.unknownIdents = initIntSet()
@ -371,7 +373,7 @@ proc makeRangeType*(c: PContext; first, last: BiggestInt;
addSonSkipIntLit(result, intType) # basetype of range addSonSkipIntLit(result, intType) # basetype of range
proc markIndirect*(c: PContext, s: PSym) {.inline.} = 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) incl(s.flags, sfAddrTaken)
# XXX add to 'c' for global analysis # 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]) localError(n.info, errXExpectsTypeOrValue, opToStr[m])
else: else:
n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType}) n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType})
var typ = skipTypes(n.sons[1].typ, abstractVarRange + var typ = skipTypes(n.sons[1].typ, abstractVarRange + {tyTypeDesc})
{tyTypeDesc, tyFieldAccessor})
case typ.kind case typ.kind
of tySequence, tyString, tyCString, tyOpenArray, tyVarargs: of tySequence, tyString, tyCString, tyOpenArray, tyVarargs:
n.typ = getSysType(tyInt) n.typ = getSysType(tyInt)
@ -255,7 +254,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
n.typ = typ.sons[0] # indextype n.typ = typ.sons[0] # indextype
of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32: of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32:
# do not skip the range! # do not skip the range!
n.typ = n.sons[1].typ.skipTypes(abstractVar + {tyFieldAccessor}) n.typ = n.sons[1].typ.skipTypes(abstractVar)
of tyGenericParam: of tyGenericParam:
# prepare this for resolving in semtypinst: # prepare this for resolving in semtypinst:
# we must use copyTree here in order to avoid creating a cycle # 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[1].typ != nil and n[1].typ.kind == tyTypeDesc and
n[2].kind in {nkStrLit..nkTripleStrLit, nkType} 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}: if n[2].kind in {nkStrLit..nkTripleStrLit}:
case n[2].strVal.normalize case n[2].strVal.normalize
@ -573,7 +572,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
optImplicitStatic notin gOptions: return optImplicitStatic notin gOptions: return
if callee.magic notin ctfeWhitelist: 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 return
if n.typ != nil and typeAllowed(n.typ, skConst) != nil: 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 typeof support.
# for ``type(countup(1,3))``, see ``tests/ttoseq``. # for ``type(countup(1,3))``, see ``tests/ttoseq``.
result = semOverloadedCall(c, n, nOrig, result = semOverloadedCall(c, n, nOrig,
{skProc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags) {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags)
else: else:
result = semOverloadedCall(c, n, nOrig, result = semOverloadedCall(c, n, nOrig,
{skProc, skMethod, skConverter, skMacro, skTemplate}, flags) {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags)
if result != nil: if result != nil:
if result.sons[0].kind != nkSym: if result.sons[0].kind != nkSym:
@ -1086,9 +1085,6 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
if field != nil: if field != nil:
n.typ = makeTypeDesc(c, field.typ) n.typ = makeTypeDesc(c, field.typ)
return n return n
#n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.typ])
#n.typ.n = copyTree(n)
#return n
else: else:
tryReadingGenericParam(ty) tryReadingGenericParam(ty)
return return
@ -1229,7 +1225,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
else: nil else: nil
if s != nil: if s != nil:
case s.kind case s.kind
of skProc, skMethod, skConverter, skIterator: of skProc, skFunc, skMethod, skConverter, skIterator:
# type parameters: partial generic specialization # type parameters: partial generic specialization
n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s) n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
result = explicitGenericInstantiation(c, n, 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 = proc semReturn(c: PContext, n: PNode): PNode =
result = n result = n
checkSonsLen(n, 1) 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): c.p.owner.kind == skIterator and c.p.owner.typ.callConv == ccClosure):
if n.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
# transform ``return expr`` to ``result = expr; return`` # 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 # Preserve the magic symbol in order to be handled in evals.nim
internalAssert n.sons[0].sym.magic == mExpandToAst internalAssert n.sons[0].sym.magic == mExpandToAst
#n.typ = getSysSym("NimNode").typ # expandedSym.getReturnType #n.typ = getSysSym("NimNode").typ # expandedSym.getReturnType
n.typ = if getCompilerProc("NimNode") != nil: sysTypeFromName"NimNode" if n.kind == nkStmtList and n.len == 1: result = n[0]
else: sysTypeFromName"PNimrodNode" else: result = n
result = n result.typ = if getCompilerProc("NimNode") != nil: sysTypeFromName"NimNode"
else: sysTypeFromName"PNimrodNode"
proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym, proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
flags: TExprFlags = {}): PNode = flags: TExprFlags = {}): PNode =
@ -2114,12 +2111,14 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
if nfSem in n.flags: return if nfSem in n.flags: return
case n.kind case n.kind
of nkIdent, nkAccQuoted: of nkIdent, nkAccQuoted:
let checks = if efNoEvaluateGeneric in flags: {checkUndeclared} let checks = if efNoEvaluateGeneric in flags:
else: {checkUndeclared, checkModule, checkAmbiguity} {checkUndeclared, checkPureEnumFields}
else:
{checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields}
var s = qualifiedLookUp(c, n, checks) var s = qualifiedLookUp(c, n, checks)
if c.matchedConcept == nil: semCaptureSym(s, c.p.owner) if c.matchedConcept == nil: semCaptureSym(s, c.p.owner)
result = semSym(c, n, s, flags) 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) #performProcvarCheck(c, n, s)
result = symChoice(c, n, s, scClosed) result = symChoice(c, n, s, scClosed)
if result.kind == nkSym: if result.kind == nkSym:
@ -2214,7 +2213,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
errorUseQualifier(c, n.info, s) errorUseQualifier(c, n.info, s)
elif s.magic == mNone: result = semDirectOp(c, n, flags) elif s.magic == mNone: result = semDirectOp(c, n, flags)
else: result = semMagic(c, n, s, flags) else: result = semMagic(c, n, s, flags)
of skProc, skMethod, skConverter, skIterator: of skProc, skFunc, skMethod, skConverter, skIterator:
if s.magic == mNone: result = semDirectOp(c, n, flags) if s.magic == mNone: result = semDirectOp(c, n, flags)
else: result = semMagic(c, n, s, flags) else: result = semMagic(c, n, s, flags)
else: else:
@ -2328,6 +2327,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkPragma: pragma(c, c.p.owner, n, stmtPragmas) of nkPragma: pragma(c, c.p.owner, n, stmtPragmas)
of nkIteratorDef: result = semIterator(c, n) of nkIteratorDef: result = semIterator(c, n)
of nkProcDef: result = semProc(c, n) of nkProcDef: result = semProc(c, n)
of nkFuncDef: result = semFunc(c, n)
of nkMethodDef: result = semMethod(c, n) of nkMethodDef: result = semMethod(c, n)
of nkConverterDef: result = semConverterDef(c, n) of nkConverterDef: result = semConverterDef(c, n)
of nkMacroDef: result = semMacroDef(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 mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n)
of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n) of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n)
of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLowerAscii, 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 mAppType: result = getAppType(n)
of mNaN: result = newFloatNodeT(NaN, n) of mNaN: result = newFloatNodeT(NaN, n)
of mInf: result = newFloatNodeT(Inf, n) of mInf: result = newFloatNodeT(Inf, n)
@ -498,7 +500,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
result = newStrNodeT(lookupSymbol(s.name), n) result = newStrNodeT(lookupSymbol(s.name), n)
else: else:
result = copyTree(s.ast) result = copyTree(s.ast)
of {skProc, skMethod}: of {skProc, skFunc, skMethod}:
result = n result = n
of skType: of skType:
# XXX gensym'ed symbols can come here and cannot be resolved. This is # 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: of nkCallKinds:
if n.sons[0].kind != nkSym: return if n.sons[0].kind != nkSym: return
var s = n.sons[0].sym var s = n.sons[0].sym
if s.kind != skProc: return if s.kind != skProc and s.kind != skFunc: return
try: try:
case s.magic case s.magic
of mNone: of mNone:

View file

@ -61,7 +61,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
of skUnknown: of skUnknown:
# Introduced in this pass! Leave it as an identifier. # Introduced in this pass! Leave it as an identifier.
result = n result = n
of skProc, skMethod, skIterator, skConverter, skModule: of skProc, skFunc, skMethod, skIterator, skConverter, skModule:
result = symChoice(c, n, s, scOpen) result = symChoice(c, n, s, scOpen)
of skTemplate: of skTemplate:
if macroToExpandSym(s): if macroToExpandSym(s):
@ -105,6 +105,10 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
result = n result = n
let ident = considerQuotedIdent(n) let ident = considerQuotedIdent(n)
var s = searchInScopes(c, ident).skipAlias(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 s == nil:
if ident.id notin ctx.toMixin and withinMixin notin flags: if ident.id notin ctx.toMixin and withinMixin notin flags:
errorUndeclaredIdentifier(c, n.info, ident.s) errorUndeclaredIdentifier(c, n.info, ident.s)
@ -239,7 +243,7 @@ proc semGenericStmt(c: PContext, n: PNode,
of skUnknown, skParam: of skUnknown, skParam:
# Leave it as an identifier. # Leave it as an identifier.
discard discard
of skProc, skMethod, skIterator, skConverter, skModule: of skProc, skFunc, skMethod, skIterator, skConverter, skModule:
result.sons[0] = sc result.sons[0] = sc
# do not check of 's.magic==mRoof' here because it might be some # do not check of 's.magic==mRoof' here because it might be some
# other '^' but after overload resolution the proper one: # 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): for j in countup(0, L-3):
addTempDecl(c, getIdentNode(a.sons[j]), skParam) addTempDecl(c, getIdentNode(a.sons[j]), skParam)
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
nkIteratorDef, nkLambdaKinds: nkFuncDef, nkIteratorDef, nkLambdaKinds:
checkSonsLen(n, bodyPos + 1) checkSonsLen(n, bodyPos + 1)
if n.sons[namePos].kind != nkEmpty: if n.sons[namePos].kind != nkEmpty:
addTempDecl(c, getIdentNode(n.sons[0]), skProc) addTempDecl(c, getIdentNode(n.sons[0]), skProc)

View file

@ -36,7 +36,7 @@ proc rawPushProcCon(c: PContext, owner: PSym) =
c.p = x c.p = x
proc rawHandleSelf(c: PContext; owner: PSym) = 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: if c.selfName != nil and owner.kind in callableSymbols and owner.typ != nil:
let params = owner.typ.n let params = owner.typ.n
if params.len > 1: if params.len > 1:

View file

@ -387,7 +387,7 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
addFactNeg(c.guards, canon(n.sons[0])) addFactNeg(c.guards, canon(n.sons[0]))
dec c.inLoop dec c.inLoop
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
nkMacroDef, nkTemplateDef, nkConstSection, nkPragma: nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef:
discard discard
else: else:
analyseSons(c, n) analyseSons(c, n)

View file

@ -835,7 +835,7 @@ proc track(tracked: PEffects, n: PNode) =
setLen(tracked.locked, oldLocked) setLen(tracked.locked, oldLocked)
tracked.currLockLevel = oldLockLevel tracked.currLockLevel = oldLockLevel
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
nkMacroDef, nkTemplateDef, nkLambda, nkDo: nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
discard discard
of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv: of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv:
if n.len == 2: track(tracked, n.sons[1]) if n.len == 2: track(tracked, n.sons[1])
@ -937,7 +937,7 @@ proc trackProc*(s: PSym, body: PNode) =
track(t, body) track(t, body)
if not isEmptyType(s.typ.sons[0]) and if not isEmptyType(s.typ.sons[0]) and
{tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} 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 var res = s.ast.sons[resultPos].sym # get result symbol
if res.id notin t.init: if res.id notin t.init:
message(body.info, warnProveInit, "result") message(body.info, warnProveInit, "result")
@ -979,10 +979,10 @@ proc trackProc*(s: PSym, body: PNode) =
message(s.info, warnLockLevel, message(s.info, warnLockLevel,
"declared lock level is $1, but real lock level is $2" % "declared lock level is $1, but real lock level is $2" %
[$s.typ.lockLevel, $t.maxLockLevel]) [$s.typ.lockLevel, $t.maxLockLevel])
when useWriteTracking: trackWrites(s, body) if s.kind == skFunc: trackWrites(s, body)
proc trackTopLevelStmt*(module: PSym; n: PNode) = 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}: nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
return return
var effects = newNode(nkEffectList, n.info) var effects = newNode(nkEffectList, n.info)

View file

@ -437,8 +437,6 @@ proc isDiscardUnderscore(v: PSym): bool =
proc semUsing(c: PContext; n: PNode): PNode = proc semUsing(c: PContext; n: PNode): PNode =
result = ast.emptyNode result = ast.emptyNode
if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "using") 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): for i in countup(0, sonsLen(n)-1):
var a = n.sons[i] var a = n.sons[i]
if gCmd == cmdIdeTools: suggestStmt(c, a) if gCmd == cmdIdeTools: suggestStmt(c, a)
@ -493,6 +491,7 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
addSon(obj.n, newSymNode(field)) addSon(obj.n, newSymNode(field))
n.sons[0] = makeDeref x n.sons[0] = makeDeref x
n.sons[1] = newSymNode(field) n.sons[1] = newSymNode(field)
n.typ = field.typ
else: else:
localError(n.info, "implicit object field construction " & localError(n.info, "implicit object field construction " &
"requires a .partial object, but got " & typeToString(obj)) "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 s.typ = n.typ
for i in 1..<params.len: for i in 1..<params.len:
if params[i].typ.kind in {tyTypeDesc, tyGenericParam, if params[i].typ.kind in {tyTypeDesc, tyGenericParam,
tyFromExpr, tyFieldAccessor}+tyTypeClasses: tyFromExpr}+tyTypeClasses:
localError(params[i].info, "cannot infer type of parameter: " & localError(params[i].info, "cannot infer type of parameter: " &
params[i].sym.name.s) params[i].sym.name.s)
#params[i].sym.owner = s #params[i].sym.owner = s
@ -1603,6 +1602,9 @@ proc semIterator(c: PContext, n: PNode): PNode =
proc semProc(c: PContext, n: PNode): PNode = proc semProc(c: PContext, n: PNode): PNode =
result = semProcAux(c, n, skProc, procPragmas) 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 = proc semMethod(c: PContext, n: PNode): PNode =
if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "method") if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "method")
result = semProcAux(c, n, skMethod, methodPragmas) 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]) a.sons[2] = semTemplBody(c, a.sons[2])
of nkProcDef, nkLambdaKinds: of nkProcDef, nkLambdaKinds:
result = semRoutineInTemplBody(c, n, skProc) result = semRoutineInTemplBody(c, n, skProc)
of nkFuncDef:
result = semRoutineInTemplBody(c, n, skFunc)
of nkMethodDef: of nkMethodDef:
result = semRoutineInTemplBody(c, n, skMethod) result = semRoutineInTemplBody(c, n, skMethod)
of nkIteratorDef: 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) if not isPure: strTableAdd(c.module.tab, e)
addSon(result.n, newSymNode(e)) addSon(result.n, newSymNode(e))
styleCheckDef(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): if isPure and strTableIncl(symbols, e):
wrongRedefinition(e.info, e.name.s) wrongRedefinition(e.info, e.name.s)
inc(counter) inc(counter)
@ -1391,6 +1393,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of mSet: result = semSet(c, n, prev) of mSet: result = semSet(c, n, prev)
of mOrdinal: result = semOrdinal(c, n, prev) of mOrdinal: result = semOrdinal(c, n, prev)
of mSeq: result = semContainer(c, n, tySequence, "seq", 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 mVarargs: result = semVarargs(c, n, prev)
of mTypeDesc: result = makeTypeDesc(c, semTypeNode(c, n[1], nil)) of mTypeDesc: result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
of mExpr: of mExpr:
@ -1590,6 +1593,8 @@ proc processMagicType(c: PContext, m: PSym) =
setMagicType(m, tySet, 0) setMagicType(m, tySet, 0)
of mSeq: of mSeq:
setMagicType(m, tySequence, 0) setMagicType(m, tySequence, 0)
of mOpt:
setMagicType(m, tyOpt, 0)
of mOrdinal: of mOrdinal:
setMagicType(m, tyOrdinal, 0) setMagicType(m, tyOrdinal, 0)
rawAddSon(m.typ, newTypeS(tyNone, c)) 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 isTypeParam(n[i]): needsFixing = true
if needsFixing: if needsFixing:
n.sons[0] = newSymNode(n.sons[0].sym.owner) 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: for i in 0 .. <n.safeLen:
n.sons[i] = reResolveCallsWithTypedescParams(cl, n[i]) 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: of tyRef, tyPtr, tyGenericBody, tyVar:
c.hashType t.lastSon, flags c.hashType t.lastSon, flags
if tfVarIsPtr in t.flags: c &= ".varisptr" if tfVarIsPtr in t.flags: c &= ".varisptr"
of tyFromExpr, tyFieldAccessor: of tyFromExpr:
c.hashTree(t.n) c.hashTree(t.n)
of tyTuple: of tyTuple:
if t.n != nil and CoType notin flags: 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 tfExplicit notin aOrig.flags
aOrig = if useTypeLoweringRuleInTypeClass: aOrig = if useTypeLoweringRuleInTypeClass:
aOrig.skipTypes({tyTypeDesc, tyFieldAccessor}) aOrig.skipTypes({tyTypeDesc})
else: else:
aOrig 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 # XXX: This is very hacky. It should be moved back into liftTypeParam
if x.kind in {tyGenericInst, tyArray} and if x.kind in {tyGenericInst, tyArray} and
c.calleeSym != nil 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) let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f)
return typeRel(c, inst, a) return typeRel(c, inst, a)
@ -1832,7 +1832,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
bothMetaCounter < 100: bothMetaCounter < 100:
lastBindingsLength = m.bindings.counter lastBindingsLength = m.bindings.counter
inc(bothMetaCounter) inc(bothMetaCounter)
if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds: if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds:
result = c.semInferredLambda(c, m.bindings, arg) result = c.semInferredLambda(c, m.bindings, arg)
elif arg.kind != nkSym: elif arg.kind != nkSym:
return nil return nil
@ -1865,7 +1865,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
else: else:
result = implicitConv(nkHiddenStdConv, f, arg, m, c) result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isInferred, isInferredConvertible: 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) result = c.semInferredLambda(c, m.bindings, arg)
elif arg.kind != nkSym: elif arg.kind != nkSym:
return nil return nil
@ -1952,7 +1952,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
z.calleeSym = m.calleeSym z.calleeSym = m.calleeSym
var best = -1 var best = -1
for i in countup(0, sonsLen(arg) - 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) copyCandidate(z, m)
z.callee = arg.sons[i].typ z.callee = arg.sons[i].typ
if tfUnresolved in z.callee.flags: continue 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 s.kind == skIterator:
if c.tooEarly: return n if c.tooEarly: return n
else: return liftIterSym(n, getCurrOwner(c)) 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': # top level .closure procs are still somewhat supported for 'Nake':
return makeClosure(s, nil, n.info) return makeClosure(s, nil, n.info)
#elif n.sym.kind in {skVar, skLet} and n.sym.typ.callConv == ccClosure: #elif n.sym.kind in {skVar, skLet} and n.sym.typ.callConv == ccClosure:

View file

@ -12,9 +12,6 @@
import import
intsets, ast, astalgo, trees, msgs, strutils, platform, renderer intsets, ast, astalgo, trees, msgs, strutils, platform, renderer
proc firstOrd*(t: PType): BiggestInt
proc lastOrd*(t: PType): BiggestInt
proc lengthOrd*(t: PType): BiggestInt
type type
TPreferedDesc* = enum TPreferedDesc* = enum
preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg
@ -48,8 +45,6 @@ type
proc equalParams*(a, b: PNode): TParamsEquality proc equalParams*(a, b: PNode): TParamsEquality
# returns whether the parameter lists of the procs a, b are exactly the same # 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 const
# TODO: Remove tyTypeDesc from each abstractX and (where necessary) # TODO: Remove tyTypeDesc from each abstractX and (where necessary)
@ -70,17 +65,6 @@ const
typedescPtrs* = abstractPtrs + {tyTypeDesc} typedescPtrs* = abstractPtrs + {tyTypeDesc}
typedescInst* = abstractInst + {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 type
TTypeFieldResult* = enum TTypeFieldResult* = enum
frNone, # type has no object type field 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 # made or intializing of the type field suffices or if there is no type field
# at all in this type. # at all in this type.
proc invalidGenericInst(f: PType): bool = proc invalidGenericInst*(f: PType): bool =
result = f.kind == tyGenericInst and lastSon(f) == nil result = f.kind == tyGenericInst and lastSon(f) == nil
proc isPureObject(typ: PType): bool = proc isPureObject*(typ: PType): bool =
var t = typ var t = typ
while t.kind == tyObject and t.sons[0] != nil: while t.kind == tyObject and t.sons[0] != nil:
t = t.sons[0].skipTypes(skipPtrs) t = t.sons[0].skipTypes(skipPtrs)
result = t.sym != nil and sfPure in t.sym.flags result = t.sym != nil and sfPure in t.sym.flags
proc getOrdValue(n: PNode): BiggestInt = proc getOrdValue*(n: PNode): BiggestInt =
case n.kind case n.kind
of nkCharLit..nkUInt64Lit: result = n.intVal of nkCharLit..nkUInt64Lit: result = n.intVal
of nkNilLit: result = 0 of nkNilLit: result = 0
@ -116,14 +100,6 @@ proc isIntLit*(t: PType): bool {.inline.} =
proc isFloatLit*(t: PType): bool {.inline.} = proc isFloatLit*(t: PType): bool {.inline.} =
result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit 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 = proc getProcHeader*(sym: PSym; prefer: TPreferedDesc = preferName): string =
result = sym.owner.name.s & '.' & sym.name.s & '(' result = sym.owner.name.s & '.' & sym.name.s & '('
var n = sym.typ.n var n = sym.typ.n
@ -151,7 +127,7 @@ proc elemType*(t: PType): PType =
else: result = t.lastSon else: result = t.lastSon
assert(result != nil) assert(result != nil)
proc isOrdinalType(t: PType): bool = proc isOrdinalType*(t: PType): bool =
assert(t != nil) assert(t != nil)
const const
# caution: uint, uint64 are no ordinal types! # caution: uint, uint64 are no ordinal types!
@ -159,7 +135,7 @@ proc isOrdinalType(t: PType): bool =
parentKinds = {tyRange, tyOrdinal, tyGenericInst, tyAlias, tyDistinct} parentKinds = {tyRange, tyOrdinal, tyGenericInst, tyAlias, tyDistinct}
t.kind in baseKinds or (t.kind in parentKinds and isOrdinalType(t.sons[0])) 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 var b = t
while b.kind in {tyRange, tyGenericInst, tyAlias}: b = b.sons[0] while b.kind in {tyRange, tyGenericInst, tyAlias}: b = b.sons[0]
result = b.kind == tyEnum and tfEnumHasHoles in b.flags 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 = proc isObjectPredicate(t: PType): bool =
result = t.kind == tyObject result = t.kind == tyObject
proc containsObject(t: PType): bool = proc containsObject*(t: PType): bool =
result = searchTypeFor(t, isObjectPredicate) result = searchTypeFor(t, isObjectPredicate)
proc isObjectWithTypeFieldPredicate(t: PType): bool = proc isObjectWithTypeFieldPredicate(t: PType): bool =
@ -297,7 +273,7 @@ proc isGCRef(t: PType): bool =
result = t.kind in GcTypeKinds or result = t.kind in GcTypeKinds or
(t.kind == tyProc and t.callConv == ccClosure) (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 # returns true if typ contains a reference, sequence or string (all the
# things that are garbage-collected) # things that are garbage-collected)
result = searchTypeFor(typ, isGCRef) result = searchTypeFor(typ, isGCRef)
@ -312,7 +288,7 @@ proc containsTyRef*(typ: PType): bool =
proc isHiddenPointer(t: PType): bool = proc isHiddenPointer(t: PType): bool =
result = t.kind in {tyString, tySequence} 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 # returns true if typ contains a string, table or sequence (all the things
# that need to be copied deeply) # that need to be copied deeply)
result = searchTypeFor(typ, isHiddenPointer) result = searchTypeFor(typ, isHiddenPointer)
@ -355,7 +331,7 @@ proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool =
of tyProc: result = typ.callConv == ccClosure of tyProc: result = typ.callConv == ccClosure
else: discard else: discard
proc canFormAcycle(typ: PType): bool = proc canFormAcycle*(typ: PType): bool =
var marker = initIntSet() var marker = initIntSet()
result = canFormAcycleAux(marker, typ, typ.id) result = canFormAcycleAux(marker, typ, typ.id)
@ -521,7 +497,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
of tyExpr: of tyExpr:
internalAssert t.len == 0 internalAssert t.len == 0
result = "untyped" result = "untyped"
of tyFromExpr, tyFieldAccessor: of tyFromExpr:
result = renderTree(t.n) result = renderTree(t.n)
of tyArray: of tyArray:
if t.sons[0].kind == tyRange: if t.sons[0].kind == tyRange:
@ -532,6 +508,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
typeToString(t.sons[1]) & ']' typeToString(t.sons[1]) & ']'
of tySequence: of tySequence:
result = "seq[" & typeToString(t.sons[0]) & ']' result = "seq[" & typeToString(t.sons[0]) & ']'
of tyOpt:
result = "opt[" & typeToString(t.sons[0]) & ']'
of tyOrdinal: of tyOrdinal:
result = "ordinal[" & typeToString(t.sons[0]) & ']' result = "ordinal[" & typeToString(t.sons[0]) & ']'
of tySet: of tySet:
@ -605,7 +583,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
result = typeToStr[t.kind] result = typeToStr[t.kind]
result.addTypeFlags(t) result.addTypeFlags(t)
proc firstOrd(t: PType): BiggestInt = proc firstOrd*(t: PType): BiggestInt =
case t.kind case t.kind
of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy: of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy:
result = 0 result = 0
@ -630,7 +608,7 @@ proc firstOrd(t: PType): BiggestInt =
else: else:
assert(t.n.sons[0].kind == nkSym) assert(t.n.sons[0].kind == nkSym)
result = t.n.sons[0].sym.position result = t.n.sons[0].sym.position
of tyGenericInst, tyDistinct, tyTypeDesc, tyFieldAccessor, tyAlias: of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias:
result = firstOrd(lastSon(t)) result = firstOrd(lastSon(t))
of tyOrdinal: of tyOrdinal:
if t.len > 0: result = firstOrd(lastSon(t)) if t.len > 0: result = firstOrd(lastSon(t))
@ -639,7 +617,7 @@ proc firstOrd(t: PType): BiggestInt =
internalError("invalid kind for first(" & $t.kind & ')') internalError("invalid kind for first(" & $t.kind & ')')
result = 0 result = 0
proc lastOrd(t: PType): BiggestInt = proc lastOrd*(t: PType): BiggestInt =
case t.kind case t.kind
of tyBool: result = 1 of tyBool: result = 1
of tyChar: result = 255 of tyChar: result = 255
@ -666,7 +644,7 @@ proc lastOrd(t: PType): BiggestInt =
of tyEnum: of tyEnum:
assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym) assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
result = t.n.sons[sonsLen(t.n) - 1].sym.position 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)) result = lastOrd(lastSon(t))
of tyProxy: result = 0 of tyProxy: result = 0
of tyOrdinal: of tyOrdinal:
@ -676,7 +654,7 @@ proc lastOrd(t: PType): BiggestInt =
internalError("invalid kind for last(" & $t.kind & ')') internalError("invalid kind for last(" & $t.kind & ')')
result = 0 result = 0
proc lengthOrd(t: PType): BiggestInt = proc lengthOrd*(t: PType): BiggestInt =
case t.kind case t.kind
of tyInt64, tyInt32, tyInt: result = lastOrd(t) of tyInt64, tyInt32, tyInt: result = lastOrd(t)
of tyDistinct: result = lengthOrd(t.sons[0]) of tyDistinct: result = lengthOrd(t.sons[0])
@ -689,6 +667,14 @@ proc lengthOrd(t: PType): BiggestInt =
else: else:
result = lastOrd(t) - firstOrd(t) + 1 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 equality -----------------------------------------------
type type
@ -989,7 +975,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
result = a.sym.position == b.sym.position result = a.sym.position == b.sym.position
of tyGenericInvocation, tyGenericBody, tySequence, of tyGenericInvocation, tyGenericBody, tySequence,
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyOpenArray, tySet, tyRef, tyPtr, tyVar,
tyArray, tyProc, tyVarargs, tyOrdinal, tyTypeClasses, tyFieldAccessor: tyArray, tyProc, tyVarargs, tyOrdinal, tyTypeClasses, tyOpt:
cycleCheck() cycleCheck()
if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n
result = sameChildrenAux(a, b, c) and sameFlags(a, b) result = sameChildrenAux(a, b, c) and sameFlags(a, b)
@ -1007,7 +993,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
cycleCheck() cycleCheck()
result = sameTypeAux(a.lastSon, b.lastSon, c) result = sameTypeAux(a.lastSon, b.lastSon, c)
of tyNone: result = false of tyNone: result = false
of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("sameFlags") of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("sameFlags")
proc sameBackendType*(x, y: PType): bool = proc sameBackendType*(x, y: PType): bool =
var c = initSameTypeClosure() var c = initSameTypeClosure()
@ -1089,7 +1075,7 @@ proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind,
of nkNone..nkNilLit: of nkNone..nkNilLit:
discard discard
else: else:
if n.kind == nkRecCase and kind in {skProc, skConst}: if n.kind == nkRecCase and kind in {skProc, skFunc, skConst}:
return n[0].typ return n[0].typ
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
let it = n.sons[i] let it = n.sons[i]
@ -1107,7 +1093,7 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
flags: TTypeAllowedFlags = {}): PType = 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 # if we have already checked the type, return true, because we stop the
# evaluation if something is wrong: # evaluation if something is wrong:
result = nil result = nil
@ -1116,7 +1102,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
var t = skipTypes(typ, abstractInst-{tyTypeDesc}) var t = skipTypes(typ, abstractInst-{tyTypeDesc})
case t.kind case t.kind
of tyVar: 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}) var t2 = skipTypes(t.sons[0], abstractInst-{tyTypeDesc})
case t2.kind case t2.kind
of tyVar: of tyVar:
@ -1144,7 +1130,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
of tyTypeClasses: of tyTypeClasses:
if not (tfGenericTypeParam in t.flags or taField notin flags): result = t if not (tfGenericTypeParam in t.flags or taField notin flags): result = t
of tyGenericBody, tyGenericParam, tyGenericInvocation, of tyGenericBody, tyGenericParam, tyGenericInvocation,
tyNone, tyForward, tyFromExpr, tyFieldAccessor: tyNone, tyForward, tyFromExpr:
result = t result = t
of tyNil: of tyNil:
if kind != skConst: result = t if kind != skConst: result = t
@ -1160,7 +1146,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
if kind != skParam: result = t if kind != skParam: result = t
else: result = typeAllowedAux(marker, t.sons[0], skVar, flags) else: result = typeAllowedAux(marker, t.sons[0], skVar, flags)
of tySequence: of tySequence, tyOpt:
if t.sons[0].kind != tyEmpty: if t.sons[0].kind != tyEmpty:
result = typeAllowedAux(marker, t.sons[0], skVar, flags+{taHeap}) result = typeAllowedAux(marker, t.sons[0], skVar, flags+{taHeap})
of tyArray: of tyArray:
@ -1176,7 +1162,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
result = typeAllowedAux(marker, t.sons[i], kind, flags) result = typeAllowedAux(marker, t.sons[i], kind, flags)
if result != nil: break if result != nil: break
of tyObject, tyTuple: 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 t.kind == tyObject and t.sons[0] != nil: return t
let flags = flags+{taField} let flags = flags+{taField}
for i in countup(0, sonsLen(t) - 1): 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 # for now same as error node; we say it's a valid type as it should
# prevent cascading errors: # prevent cascading errors:
result = nil result = nil
of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("typeAllowedAux") of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("typeAllowedAux")
proc typeAllowed*(t: PType, kind: TSymKind): PType = proc typeAllowed*(t: PType, kind: TSymKind): PType =
# returns 'nil' on success and otherwise the part of the type that is # returns 'nil' on success and otherwise the part of the type that is
@ -1199,6 +1185,63 @@ proc typeAllowed*(t: PType, kind: TSymKind): PType =
proc align(address, alignment: BiggestInt): BiggestInt = proc align(address, alignment: BiggestInt): BiggestInt =
result = (address + (alignment - 1)) and not (alignment - 1) 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 const
szNonConcreteType* = -3 szNonConcreteType* = -3
szIllegalRecursion* = -2 szIllegalRecursion* = -2
@ -1353,13 +1396,21 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
of tyStatic: of tyStatic:
result = if typ.n != nil: computeSizeAux(typ.lastSon, a) result = if typ.n != nil: computeSizeAux(typ.lastSon, a)
else: szUnknownSize 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: else:
#internalError("computeSizeAux()") #internalError("computeSizeAux()")
result = szUnknownSize result = szUnknownSize
typ.size = result typ.size = result
typ.align = int16(a) typ.align = int16(a)
proc computeSize(typ: PType): BiggestInt = proc computeSize*(typ: PType): BiggestInt =
var a: BiggestInt = 1 var a: BiggestInt = 1
result = computeSizeAux(typ, a) result = computeSizeAux(typ, a)
@ -1368,7 +1419,7 @@ proc getReturnType*(s: PSym): PType =
assert s.kind in skProcKinds assert s.kind in skProcKinds
result = s.typ.sons[0] result = s.typ.sons[0]
proc getSize(typ: PType): BiggestInt = proc getSize*(typ: PType): BiggestInt =
result = computeSize(typ) result = computeSize(typ)
if result < 0: internalError("getSize: " & $typ.kind) if result < 0: internalError("getSize: " & $typ.kind)

View file

@ -19,7 +19,7 @@ import ast except getstr
import import
strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes,
parser, vmdeps, idents, trees, renderer, options, transf, parseutils, parser, vmdeps, idents, trees, renderer, options, transf, parseutils,
vmmarshal vmmarshal, gorgeimpl
from semfold import leValueConv, ordinalValToString from semfold import leValueConv, ordinalValToString
from evaltempl import evalTemplate 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) stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1)
var info = c.debug[pc] var info = c.debug[pc]
# we now use the same format as in system/except.nim # 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) var line = toLinenumber(info)
if line > 0: if line > 0:
add(s, '(') add(s, '(')
@ -979,7 +982,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let node = regs[rb+i].regToNode let node = regs[rb+i].regToNode
node.info = c.debug[pc] node.info = c.debug[pc]
macroCall.add(node) 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 a.recSetFlagIsRef
ensureKind(rkNode) ensureKind(rkNode)
regs[ra].node = a regs[ra].node = a

View file

@ -7,50 +7,7 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
import ast, types, msgs, os, osproc, streams, options, idents, securehash import ast, types, msgs, os, streams, options, idents
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)
proc opSlurp*(file: string, info: TLineInfo, module: PSym): string = proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
try: try:
@ -250,6 +207,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
result = mapTypeToBracket("ref", mRef, t, info) result = mapTypeToBracket("ref", mRef, t, info)
of tyVar: result = mapTypeToBracket("var", mVar, t, info) of tyVar: result = mapTypeToBracket("var", mVar, t, info)
of tySequence: result = mapTypeToBracket("seq", mSeq, t, info) of tySequence: result = mapTypeToBracket("seq", mSeq, t, info)
of tyOpt: result = mapTypeToBracket("opt", mOpt, t, info)
of tyProc: of tyProc:
if inst: if inst:
result = newNodeX(nkProcTy) result = newNodeX(nkProcTy)
@ -304,7 +262,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
of tyNot: result = mapTypeToBracket("not", mNot, t, info) of tyNot: result = mapTypeToBracket("not", mNot, t, info)
of tyAnything: result = atomicType("anything", mNone) of tyAnything: result = atomicType("anything", mNone)
of tyInferred: internalAssert false of tyInferred: internalAssert false
of tyStatic, tyFromExpr, tyFieldAccessor: of tyStatic, tyFromExpr:
if inst: if inst:
if t.n != nil: result = t.n.copyTree if t.n != nil: result = t.n.copyTree
else: result = atomicType("void", mVoid) else: result = atomicType("void", mVoid)
@ -313,7 +271,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
result.add atomicType("static", mNone) result.add atomicType("static", mNone)
if t.n != nil: if t.n != nil:
result.add t.n.copyTree result.add t.n.copyTree
of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("mapTypeToAstX") of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("mapTypeToAstX")
proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode = proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode =
result = mapTypeToAstX(t, info, false, true) result = mapTypeToAstX(t, info, false, true)

View file

@ -1289,7 +1289,7 @@ proc checkCanEval(c: PCtx; n: PNode) =
if s.kind in {skVar, skTemp, skLet, skParam, skResult} and 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: not s.isOwnedBy(c.prc.sym) and s.owner != c.module and c.mode != emRepl:
cannotEval(n) cannotEval(n)
elif s.kind in {skProc, skConverter, skMethod, elif s.kind in {skProc, skFunc, skConverter, skMethod,
skIterator} and sfForward in s.flags: skIterator} and sfForward in s.flags:
cannotEval(n) cannotEval(n)
@ -1537,6 +1537,8 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
addSon(result, getNullValue(t.sons[i], info)) addSon(result, getNullValue(t.sons[i], info))
of tySet: of tySet:
result = newNodeIT(nkCurly, info, t) result = newNodeIT(nkCurly, info, t)
of tyOpt:
result = newNodeIT(nkNilLit, info, t)
else: else:
globalError(info, "cannot create null element for: " & $t.kind) 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 case s.kind
of skVar, skForVar, skTemp, skLet, skParam, skResult: of skVar, skForVar, skTemp, skLet, skParam, skResult:
genRdVar(c, n, dest, flags) 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: # 'skTemplate' is only allowed for 'getAst' support:
if procIsCallback(c, s): discard if procIsCallback(c, s): discard
elif sfImportc in s.flags: c.importcSym(n.info, s) elif sfImportc in s.flags: c.importcSym(n.info, s)

View file

@ -30,7 +30,7 @@ type
wMacro, wMethod, wMixin, wMod, wNil, wMacro, wMethod, wMixin, wMod, wNil,
wNot, wNotin, wObject, wOf, wOr, wOut, wProc, wPtr, wRaise, wRef, wReturn, wNot, wNotin, wObject, wOf, wOr, wOut, wProc, wPtr, wRaise, wRef, wReturn,
wShl, wShr, wStatic, wTemplate, wTry, wTuple, wType, wUsing, wVar, wShl, wShr, wStatic, wTemplate, wTry, wTuple, wType, wUsing, wVar,
wWhen, wWhile, wWith, wWithout, wXor, wYield, wWhen, wWhile, wXor, wYield,
wColon, wColonColon, wEquals, wDot, wDotDot, wColon, wColonColon, wEquals, wDot, wDotDot,
wStar, wMinus, wStar, wMinus,
@ -116,7 +116,7 @@ const
"out", "proc", "ptr", "raise", "ref", "return", "out", "proc", "ptr", "raise", "ref", "return",
"shl", "shr", "static", "shl", "shr", "static",
"template", "try", "tuple", "type", "using", "var", "template", "try", "tuple", "type", "using", "var",
"when", "while", "with", "without", "xor", "when", "while", "xor",
"yield", "yield",
":", "::", "=", ".", "..", ":", "::", "=", ".", "..",

View file

@ -248,6 +248,8 @@ proc markWriteOrEscape(w: var W) =
for p in a.dest: for p in a.dest:
if p.kind == skParam and p.owner == w.owner: if p.kind == skParam and p.owner == w.owner:
incl(p.flags, sfWrittenTo) 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 != {}: if {rootIsResultOrParam, rootIsHeapAccess, markAsEscaping}*a.destInfo != {}:
var destIsParam = false var destIsParam = false

View file

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

View file

@ -633,6 +633,9 @@ the ``vtptr`` magic produced types bound to ``ptr`` types.
Symbol lookup in generics Symbol lookup in generics
------------------------- -------------------------
Open and Closed symbols
~~~~~~~~~~~~~~~~~~~~~~~
The symbol binding rules in generics are slightly subtle: There are "open" and 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 "closed" symbols. A "closed" symbol cannot be re-bound in the instantiation
context, an "open" symbol can. Per default overloaded symbols are open 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 compiles as the instantiation takes the currently defined symbols into account
too. too.
Mixin statement
---------------
A symbol can be forced to be open by a `mixin`:idx: declaration: A symbol can be forced to be open by a `mixin`:idx: declaration:
.. code-block:: nim .. code-block:: nim

View file

@ -547,9 +547,6 @@ Instead of:
Using statement 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 using statement provides syntactic convenience in modules where
the same parameter names and types are used over and over. Instead of: 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.: ``Node`` etc.:
.. code-block:: nim .. code-block:: nim
{.experimental.}
using using
c: Context c: Context
n: Node 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 assignment operator for tuples copies each component.
The default assignment operator for objects copies each component. Overloading The default assignment operator for objects copies each component. Overloading
of the assignment operator for objects is not possible, but this will change of the assignment operator is described in `type-bound-operations-operator`_.
in future versions of the compiler.
.. code-block:: nim .. 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 var x = "abc" # introduces a new variable `x` and assigns a value to it
x = "xyz" # assigns a new value to `x` x = "xyz" # assigns a new value to `x`
``=`` is the *assignment operator*. The assignment operator cannot be ``=`` is the *assignment operator*. The assignment operator can be
overloaded, overwritten or forbidden, but this might change in a future version overloaded. You can declare multiple variables with a single assignment
of Nim. You can declare multiple variables with a single assignment
statement and all the variables will have the same value: statement and all the variables will have the same value:
.. code-block:: .. code-block::

View file

@ -262,6 +262,9 @@ proc buildTools(latest: bool) =
let nimgrepExe = "bin/nimgrep".exe let nimgrepExe = "bin/nimgrep".exe
nimexec "c -o:" & nimgrepExe & " tools/nimgrep.nim" nimexec "c -o:" & nimgrepExe & " tools/nimgrep.nim"
when defined(windows): buildVccTool() when defined(windows): buildVccTool()
nimexec "c -o:" & ("bin/nimresolve".exe) & " tools/nimresolve.nim"
buildNimble(latest) buildNimble(latest)
proc nsis(args: string) = proc nsis(args: string) =

View file

@ -75,7 +75,8 @@ type
nnkClosure, nnkClosure,
nnkGotoState, nnkGotoState,
nnkState, nnkState,
nnkBreakState nnkBreakState,
nnkFuncDef
NimNodeKinds* = set[NimNodeKind] NimNodeKinds* = set[NimNodeKind]
NimTypeKind* = enum # some types are no longer used, see ast.nim NimTypeKind* = enum # some types are no longer used, see ast.nim
@ -96,14 +97,14 @@ type
ntyError, ntyError,
ntyBuiltinTypeClass, ntyUserTypeClass, ntyUserTypeClassInst, ntyBuiltinTypeClass, ntyUserTypeClass, ntyUserTypeClassInst,
ntyCompositeTypeClass, ntyInferred, ntyAnd, ntyOr, ntyNot, ntyCompositeTypeClass, ntyInferred, ntyAnd, ntyOr, ntyNot,
ntyAnything, ntyStatic, ntyFromExpr, ntyFieldAccessor, ntyVoid ntyAnything, ntyStatic, ntyFromExpr, ntyOpt, ntyVoid
TNimTypeKinds* {.deprecated.} = set[NimTypeKind] TNimTypeKinds* {.deprecated.} = set[NimTypeKind]
NimSymKind* = enum NimSymKind* = enum
nskUnknown, nskConditional, nskDynLib, nskParam, nskUnknown, nskConditional, nskDynLib, nskParam,
nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet, nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet,
nskConst, nskResult, nskConst, nskResult,
nskProc, nskMethod, nskIterator, nskProc, nskFunc, nskMethod, nskIterator,
nskConverter, nskMacro, nskTemplate, nskField, nskConverter, nskMacro, nskTemplate, nskField,
nskEnumField, nskForVar, nskLabel, nskEnumField, nskForVar, nskLabel,
nskStub nskStub
@ -843,7 +844,8 @@ proc last*(node: NimNode): NimNode {.compileTime.} = node[<node.len]
const const
RoutineNodes* = {nnkProcDef, nnkMethodDef, nnkDo, nnkLambda, nnkIteratorDef, nnkTemplateDef, nnkConverterDef} RoutineNodes* = {nnkProcDef, nnkFuncDef, nnkMethodDef, nnkDo, nnkLambda,
nnkIteratorDef, nnkTemplateDef, nnkConverterDef}
AtomicNodes* = {nnkNone..nnkNilLit} AtomicNodes* = {nnkNone..nnkNilLit}
CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand, CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
nnkCallStrLit, nnkHiddenCallConv} nnkCallStrLit, nnkHiddenCallConv}

View file

@ -54,6 +54,7 @@ type
akUInt16 = 42, ## any represents an unsigned in16 akUInt16 = 42, ## any represents an unsigned in16
akUInt32 = 43, ## any represents an unsigned int32 akUInt32 = 43, ## any represents an unsigned int32
akUInt64 = 44, ## any represents an unsigned int64 akUInt64 = 44, ## any represents an unsigned int64
# akOpt = 44+18 ## the builtin 'opt' type.
Any* = object ## can represent any nim value; NOTE: the wrapped Any* = object ## can represent any nim value; NOTE: the wrapped
## value can be modified with its wrapper! This means ## value can be modified with its wrapper! This means

View file

@ -31,8 +31,12 @@ struct Nim::SysThread
void entry() override { void entry() override {
(_func)(_arg); } (_func)(_arg); }
Thread(Genode::Env &env, Genode::size_t stack_size, Entry func, void *arg) Thread(Genode::Env &env, Genode::size_t stack_size, Entry func, void *arg, int affinity)
: Genode::Thread(env, "nim-thread", stack_size), _func(func), _arg(arg) : 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(); Genode::Thread::start();
} }
@ -40,8 +44,8 @@ struct Nim::SysThread
Genode::Constructible<Thread> _thread; Genode::Constructible<Thread> _thread;
void initThread(Genode::Env *env, Genode::size_t stack_size, Entry func, void *arg) { void initThread(Genode::Env *env, Genode::size_t stack_size, Entry func, void *arg, int aff) {
_thread.construct(*env, stack_size, func, arg); } _thread.construct(*env, stack_size, func, arg, aff); }
void joinThread() { void joinThread() {
_thread->join(); } _thread->join(); }

View file

@ -129,7 +129,8 @@ proc tryExec*(db: DbConn, query: SqlQuery,
var q = dbFormat(query, args) var q = dbFormat(query, args)
var stmt: sqlite3.Pstmt var stmt: sqlite3.Pstmt
if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK: 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 result = finalize(stmt) == SQLITE_OK
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {. 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: result = quote do:
(`dotOp`(`obj`, `field`))() (`dotOp`(`obj`, `field`))()
for elem in args: for elem in args:
result[0].add elem result.add elem
# Iterators: # Iterators:
@ -471,7 +471,7 @@ macro bindMethod*(procedure: typed): auto =
# construct the `this` parameter: # construct the `this` parameter:
thisQuote = quote do: thisQuote = quote do:
var `this` {. nodecl, importc .} : `thisType` 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 # construct the procedure call inside the method
if args.len > 2: if args.len > 2:
for idx in 2..args.len-1: for idx in 2..args.len-1:
@ -483,6 +483,6 @@ macro bindMethod*(procedure: typed): auto =
params, params,
rawProc[4], rawProc[4],
rawProc[5], rawProc[5],
newTree(nnkStmtList, thisQuote[0], call) newTree(nnkStmtList, thisQuote, call)
) )
result = body result = body

View file

@ -373,11 +373,13 @@ static N_INLINE(NI32, float32ToInt32)(float x) {
#define float64ToInt64(x) ((NI64) (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) \ #define STRING_LITERAL(name, str, length) \
static const struct { \ static const struct { \
TGenericSeq Sup; \ TGenericSeq Sup; \
NIM_CHAR data[(length) + 1]; \ NIM_CHAR data[(length) + 1]; \
} name = {{length, length}, str} } name = {{length, (NI) ((NU)length | NIM_STRLIT_FLAG)}, str}
typedef struct TStringDesc* string; typedef struct TStringDesc* string;

View file

@ -58,8 +58,8 @@ const
"interface", "is", "isnot", "iterator", "let", "macro", "method", "interface", "is", "isnot", "iterator", "let", "macro", "method",
"mixin", "mod", "nil", "not", "notin", "object", "of", "or", "out", "proc", "mixin", "mod", "nil", "not", "notin", "object", "of", "or", "out", "proc",
"ptr", "raise", "ref", "return", "shl", "shr", "static", "ptr", "raise", "ref", "return", "shl", "shr", "static",
"template", "try", "tuple", "type", "using", "var", "when", "while", "with", "template", "try", "tuple", "type", "using", "var", "when", "while",
"without", "xor", "yield"] "xor", "yield"]
proc getSourceLanguage*(name: string): SourceLanguage = proc getSourceLanguage*(name: string): SourceLanguage =
for i in countup(succ(low(SourceLanguage)), high(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: if i >= 0:
result = false result = false
s.data[i].hcode = 0
s.data[i].key = default(type(s.data[i].key))
dec(s.counter) dec(s.counter)
while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1 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 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 if isEmpty(s.data[i].hcode): # end of collision cluster; So all done
return return
r = s.data[i].hcode and msk # "home" location of key@i 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 = proc missingOrExcl*[A](s: var HashSet[A], key: A): bool =
## Excludes `key` in the set `s` and tells if `key` was removed from `s`. ## 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.} = template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} =
var h = s.first var h = s.first
var idx = 0
while h >= 0: while h >= 0:
var nxt = s.data[h].next var nxt = s.data[h].next
if isFilled(s.data[h].hcode): yieldStmt if isFilled(s.data[h].hcode):
yieldStmt
inc(idx)
h = nxt h = nxt
iterator items*[A](s: OrderedSet[A]): A = iterator items*[A](s: OrderedSet[A]): A =
@ -689,6 +690,11 @@ iterator items*[A](s: OrderedSet[A]): A =
forAllOrderedPairs: forAllOrderedPairs:
yield s.data[h].key 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.} = proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: Hash): int {.inline.} =
rawGetKnownHCImpl() 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." assert other.isValid, "The set `other` needs to be initialized."
for item in other: incl(s, item) 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 = proc containsOrIncl*[A](s: var OrderedSet[A], key: A): bool =
## Includes `key` in the set `s` and tells if `key` was added to `s`. ## 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 == b.card
assert a.len == 2 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: #block orderedSetIterator:
# var a = initOrderedSet[int]() # var a = initOrderedSet[int]()
# for value in [9, 2, 1, 5, 1, 8, 4, 2]: # 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): if s <= i or mustRehash(s, i):
echo "performance issue: rightSize() will not elide enlarge() at ", 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): when not defined(testing):
echo "Micro tests run successfully." 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 if n.isNil or n.kind != JArray: return default
else: return n.elems 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 = proc `%`*(s: string): JsonNode =
## Generic constructor for JSON data. Creates a new `JString JsonNode`. ## Generic constructor for JSON data. Creates a new `JString JsonNode`.
new(result) new(result)
@ -759,6 +769,19 @@ proc `%`*[T](elements: openArray[T]): JsonNode =
result = newJArray() result = newJArray()
for elem in elements: result.add(%elem) 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 = proc `%`*(o: object): JsonNode =
## Generic constructor for JSON data. Creates a new `JObject JsonNode` ## Generic constructor for JSON data. Creates a new `JObject JsonNode`
result = newJObject() result = newJObject()
@ -779,27 +802,25 @@ proc `%`*(o: enum): JsonNode =
proc toJson(x: NimNode): NimNode {.compiletime.} = proc toJson(x: NimNode): NimNode {.compiletime.} =
case x.kind case x.kind
of nnkBracket: # array of nnkBracket: # array
if x.len == 0: return newCall(bindSym"newJArray")
result = newNimNode(nnkBracket) result = newNimNode(nnkBracket)
for i in 0 .. <x.len: for i in 0 .. <x.len:
result.add(toJson(x[i])) result.add(toJson(x[i]))
result = newCall(bindSym"%", result)
of nnkTableConstr: # object of nnkTableConstr: # object
if x.len == 0: return newCall(bindSym"newJObject")
result = newNimNode(nnkTableConstr) result = newNimNode(nnkTableConstr)
for i in 0 .. <x.len: for i in 0 .. <x.len:
x[i].expectKind nnkExprColonExpr 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 of nnkCurly: # empty object
result = newNimNode(nnkTableConstr)
x.expectLen(0) x.expectLen(0)
result = newCall(bindSym"newJObject")
of nnkNilLit: of nnkNilLit:
result = newCall("newJNull") result = newCall(bindSym"newJNull")
else: else:
result = x result = newCall(bindSym"%", x)
result = prefix(result, "%")
macro `%*`*(x: untyped): untyped = macro `%*`*(x: untyped): untyped =
## Convert an expression to a JsonNode directly, without having to specify ## 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) proc existsKey*(node: JsonNode, key: string): bool {.deprecated.} = node.hasKey(key)
## Deprecated for `hasKey` ## 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.} = proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} =
## Sets a field from a `JObject`. ## Sets a field from a `JObject`.
assert(obj.kind == JObject) assert(obj.kind == JObject)
@ -1203,7 +1214,7 @@ proc parseJson(p: var JsonParser): JsonNode =
raiseParseErr(p, "{") raiseParseErr(p, "{")
when not defined(js): 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 ## Parses from a stream `s` into a `JsonNode`. `filename` is only needed
## for nice error messages. ## for nice error messages.
## If `s` contains extra data, it will raise `JsonParsingError`. ## If `s` contains extra data, it will raise `JsonParsingError`.
@ -1934,4 +1945,8 @@ when isMainModule:
except JsonParsingError: except JsonParsingError:
doAssert getCurrentExceptionMsg().contains(errorMessages[errEofExpected]) doAssert getCurrentExceptionMsg().contains(errorMessages[errEofExpected])
# bug #6438
doAssert($ %*[] == "[]")
doAssert($ %*{} == "{}")
echo("Tests succeeded!") 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. # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
var buf: array[64, char] var buf: array[64, char]
if inet_ntop(name.sin6_family.cint, 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()) raiseOSError(osLastError())
result = ($buf, Port(nativesockets.ntohs(name.sin6_port))) result = ($buf, Port(nativesockets.ntohs(name.sin6_port)))
else: else:
@ -534,7 +534,7 @@ proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
# Cannot use INET6_ADDRSTRLEN here, because it's a C define. # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
var buf: array[64, char] var buf: array[64, char]
if inet_ntop(name.sin6_family.cint, 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()) raiseOSError(osLastError())
result = ($buf, Port(nativesockets.ntohs(name.sin6_port))) result = ($buf, Port(nativesockets.ntohs(name.sin6_port)))
else: else:

View file

@ -15,7 +15,7 @@
## A value of type ``Option[T]`` either contains a value `x` (represented as ## A value of type ``Option[T]`` either contains a value `x` (represented as
## ``some(x)``) or is empty (``none(T)``). ## ``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 ## absence of a value is often represented by ``nil``, but it is not always
## available, nor is it always a good solution. ## available, nor is it always a good solution.
## ##
@ -67,10 +67,8 @@
## assert(false) # This will not be reached ## assert(false) # This will not be reached
## except UnpackError: # Because an exception is raised ## except UnpackError: # Because an exception is raised
## discard ## discard
import typetraits import typetraits
type type
Option*[T] = object Option*[T] = object
## An optional type that stores its value and state separately in a boolean. ## An optional type that stores its value and state separately in a boolean.
@ -78,7 +76,6 @@ type
has: bool has: bool
UnpackError* = ref object of ValueError UnpackError* = ref object of ValueError
proc some*[T](val: T): Option[T] = proc some*[T](val: T): Option[T] =
## Returns a ``Option`` that has this value. ## Returns a ``Option`` that has this value.
result.has = true result.has = true
@ -88,14 +85,12 @@ proc none*(T: typedesc): Option[T] =
## Returns a ``Option`` for this type that has no value. ## Returns a ``Option`` for this type that has no value.
result.has = false result.has = false
proc isSome*[T](self: Option[T]): bool = proc isSome*[T](self: Option[T]): bool =
self.has self.has
proc isNone*[T](self: Option[T]): bool = proc isNone*[T](self: Option[T]): bool =
not self.has not self.has
proc unsafeGet*[T](self: Option[T]): T = proc unsafeGet*[T](self: Option[T]): T =
## Returns the value of a ``some``. Behavior is undefined for ``none``. ## Returns the value of a ``some``. Behavior is undefined for ``none``.
assert self.isSome assert self.isSome
@ -110,12 +105,11 @@ proc get*[T](self: Option[T]): T =
proc get*[T](self: Option[T], otherwise: T): T = proc get*[T](self: Option[T], otherwise: T): T =
## Returns the contents of this option or `otherwise` if the option is none. ## Returns the contents of this option or `otherwise` if the option is none.
if self.isSome: if self.has:
self.val self.val
else: else:
otherwise otherwise
proc map*[T](self: Option[T], callback: proc (input: T)) = proc map*[T](self: Option[T], callback: proc (input: T)) =
## Applies a callback to the value in this Option ## Applies a callback to the value in this Option
if self.has: 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] = 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 ## 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: if self.has:
some[R]( callback(self.val) ) some[R](callback(self.val))
else: else:
none(R) 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] = 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 ## 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 ## `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: else:
self self
proc `==`*(a, b: Option): bool = proc `==`*(a, b: Option): bool =
## Returns ``true`` if both ``Option``s are ``none``, ## Returns ``true`` if both ``Option``s are ``none``,
## or if they have equal values ## or if they have equal values
(a.has and b.has and a.val == b.val) or (not a.has and not b.has) (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. ## Returns the contents of this option or `otherwise` if the option is none.
if self.has: if self.has:
"Some(" & $self.val & ")" "Some(" & $self.val & ")"
else: else:
"None[" & T.name & "]" "None[" & T.name & "]"
when isMainModule: when isMainModule:
import unittest, sequtils import unittest, sequtils
@ -198,12 +204,12 @@ when isMainModule:
check false check false
test "get with a default value": test "get with a default value":
check( some("Correct").get("Wrong") == "Correct" ) check(some("Correct").get("Wrong") == "Correct")
check( stringNone.get("Correct") == "Correct" ) check(stringNone.get("Correct") == "Correct")
test "$": test "$":
check( $(some("Correct")) == "Some(Correct)" ) check($(some("Correct")) == "Some(Correct)")
check( $(stringNone) == "None[string]" ) check($(stringNone) == "None[string]")
test "map with a void result": test "map with a void result":
var procRan = 0 var procRan = 0
@ -212,11 +218,38 @@ when isMainModule:
intNone.map(proc (v: int) = check false) intNone.map(proc (v: int) = check false)
test "map": test "map":
check( some(123).map(proc (v: int): int = v * 2) == some(246) ) check(some(123).map(proc (v: int): int = v * 2) == some(246))
check( intNone.map(proc (v: int): int = v * 2).isNone ) check(intNone.map(proc (v: int): int = v * 2).isNone)
test "filter": test "filter":
check( some(123).filter(proc (v: int): bool = v == 123) == some(123) ) check(some(123).filter(proc (v: int): bool = v == 123) == some(123))
check( some(456).filter(proc (v: int): bool = v == 123).isNone ) check(some(456).filter(proc (v: int): bool = v == 123).isNone)
check( intNone.filter(proc (v: int): bool = check false).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.readDataImpl = hsReadData
result.writeDataImpl = hsWriteData result.writeDataImpl = hsWriteData
proc buildCommandLine(a: string, args: openArray[string]): cstring = proc buildCommandLine(a: string, args: openArray[string]): string =
var res = quoteShell(a) result = quoteShell(a)
for i in 0..high(args): for i in 0..high(args):
res.add(' ') result.add(' ')
res.add(quoteShell(args[i])) result.add(quoteShell(args[i]))
result = cast[cstring](alloc0(res.len+1))
copyMem(result, cstring(res), res.len)
proc buildEnv(env: StringTableRef): tuple[str: cstring, len: int] = proc buildEnv(env: StringTableRef): tuple[str: cstring, len: int] =
var L = 0 var L = 0
@ -540,11 +538,13 @@ when defined(Windows) and not defined(useNimRtl):
result.errHandle = FileHandle(si.hStdError) result.errHandle = FileHandle(si.hStdError)
var cmdl: cstring var cmdl: cstring
var cmdRoot: string
if poEvalCommand in options: if poEvalCommand in options:
cmdl = command cmdl = command
assert args.len == 0 assert args.len == 0
else: else:
cmdl = buildCommandLine(command, args) cmdRoot = buildCommandLine(command, args)
cmdl = cstring(cmdRoot)
var wd: cstring = nil var wd: cstring = nil
var e = (str: nil.cstring, len: -1) var e = (str: nil.cstring, len: -1)
if len(workingDir) > 0: wd = workingDir if len(workingDir) > 0: wd = workingDir

View file

@ -888,7 +888,7 @@ proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect,
n = x n = x
result = newString(len) result = newString(len)
for j in countdown(len-1, 0): 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 n = n shr 4
# handle negative overflow # handle negative overflow
if n == 0 and x < 0: n = -1 if n == 0 and x < 0: n = -1

View file

@ -509,10 +509,6 @@ macro check*(conditions: untyped): untyped =
## "AKB48".toLowerAscii() == "akb48" ## "AKB48".toLowerAscii() == "akb48"
## 'C' in teams ## 'C' in teams
let checked = callsite()[1] let checked = callsite()[1]
var
argsAsgns = newNimNode(nnkStmtList)
argsPrintOuts = newNimNode(nnkStmtList)
counter = 0
template asgn(a: untyped, value: typed) = template asgn(a: untyped, value: typed) =
var a = value # XXX: we need "var: var" here in order to 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)): when compiles(string($value)):
checkpoint(name & " was " & $value) checkpoint(name & " was " & $value)
proc inspectArgs(exp: NimNode): NimNode = proc inspectArgs(exp: NimNode): tuple[assigns, check, printOuts: NimNode] =
result = copyNimTree(exp) result.check = copyNimTree(exp)
result.assigns = newNimNode(nnkStmtList)
result.printOuts = newNimNode(nnkStmtList)
var counter = 0
if exp[0].kind == nnkIdent and if exp[0].kind == nnkIdent and
$exp[0] in ["and", "or", "not", "in", "notin", "==", "<=", $exp[0] in ["not", "in", "notin", "==", "<=",
">=", "<", ">", "!=", "is", "isnot"]: ">=", "<", ">", "!=", "is", "isnot"]:
for i in countup(1, exp.len - 1):
for i in 1 ..< exp.len:
if exp[i].kind notin nnkLiterals: if exp[i].kind notin nnkLiterals:
inc counter inc counter
var arg = newIdentNode(":p" & $counter) let argStr = exp[i].toStrLit
var argStr = exp[i].toStrLit let paramAst = exp[i]
var paramAst = exp[i]
if exp[i].kind == nnkIdent: if exp[i].kind == nnkIdent:
argsPrintOuts.add getAst(print(argStr, paramAst)) result.printOuts.add getAst(print(argStr, paramAst))
if exp[i].kind in nnkCallKinds: if exp[i].kind in nnkCallKinds + { nnkDotExpr, nnkBracketExpr }:
var callVar = newIdentNode(":c" & $counter) let callVar = newIdentNode(":c" & $counter)
argsAsgns.add getAst(asgn(callVar, paramAst)) result.assigns.add getAst(asgn(callVar, paramAst))
result[i] = callVar result.check[i] = callVar
argsPrintOuts.add getAst(print(argStr, callVar)) result.printOuts.add getAst(print(argStr, callVar))
if exp[i].kind == nnkExprEqExpr: if exp[i].kind == nnkExprEqExpr:
# ExprEqExpr # ExprEqExpr
# Ident !"v" # Ident !"v"
# IntLit 2 # IntLit 2
result[i] = exp[i][1] result.check[i] = exp[i][1]
if exp[i].typekind notin {ntyTypeDesc}: if exp[i].typekind notin {ntyTypeDesc}:
argsAsgns.add getAst(asgn(arg, paramAst)) let arg = newIdentNode(":p" & $counter)
argsPrintOuts.add getAst(print(argStr, arg)) result.assigns.add getAst(asgn(arg, paramAst))
result.printOuts.add getAst(print(argStr, arg))
if exp[i].kind != nnkExprEqExpr: if exp[i].kind != nnkExprEqExpr:
result[i] = arg result.check[i] = arg
else: else:
result[i][1] = arg result.check[i][1] = arg
case checked.kind case checked.kind
of nnkCallKinds: 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 (assigns, check, printOuts) = inspectArgs(checked)
let parameterizedCheck = inspectArgs(checked) let lineinfo = newStrLitNode(checked.lineinfo)
result = getAst(rewrite(parameterizedCheck, checked.lineinfo, checkedStr, let callLit = checked.toStrLit
argsAsgns, argsPrintOuts)) result = quote do:
block:
`assigns`
if not `check`:
checkpoint(`lineinfo` & ": Check failed: " & `callLit`)
`printOuts`
fail()
of nnkStmtList: of nnkStmtList:
result = newNimNode(nnkStmtList) result = newNimNode(nnkStmtList)
for i in countup(0, checked.len - 1): for node in checked:
if checked[i].kind != nnkCommentStmt: if node.kind != nnkCommentStmt:
result.add(newCall(!"check", checked[i])) result.add(newCall(!"check", node))
else: else:
template rewrite(exp, lineInfoLit, expLit) = let lineinfo = newStrLitNode(checked.lineinfo)
if not exp: let callLit = checked.toStrLit
checkpoint(lineInfoLit & ": Check failed: " & expLit)
fail()
result = getAst(rewrite(checked, checked.lineinfo, checked.toStrLit)) result = quote do:
if not `checked`:
checkpoint(`lineinfo` & ": Check failed: " & `callLit`)
fail()
template require*(conditions: untyped) = template require*(conditions: untyped) =
## Same as `check` except any failed test causes the program to quit ## 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] UncheckedArray* {.unchecked.}[T] = array[0, T]
## Array with no bounds checking ## Array with no bounds checking
when defined(nimHasOpt):
type opt*{.magic: "Opt".}[T]
proc high*[T: Ordinal](x: T): T {.magic: "High", noSideEffect.} proc high*[T: Ordinal](x: T): T {.magic: "High", noSideEffect.}
## returns the highest possible index of an array, a sequence, a string or ## 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 ## 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): when not defined(JS) and not defined(nimscript):
template space(s: PGenericSeq): int {.dirty.} = template space(s: PGenericSeq): int {.dirty.} =
s.reserved and not seqShallowFlag s.reserved and not (seqShallowFlag or strlitFlag)
include "system/hti" include "system/hti"
type type
@ -718,7 +720,7 @@ proc len*[TOpenArray: openArray|varargs](x: TOpenArray): int {.
magic: "LengthOpenArray", noSideEffect.} magic: "LengthOpenArray", noSideEffect.}
proc len*(x: string): int {.magic: "LengthStr", noSideEffect.} proc len*(x: string): int {.magic: "LengthStr", noSideEffect.}
proc len*(x: cstring): 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.} proc len*[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.}
## returns the length of an array, an openarray, a sequence or a string. ## 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 ## 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". ## "amd64", "mips", "mipsel", "arm", "arm64", "mips64", "mips64el".
seqShallowFlag = low(int) 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.} {.push profiler: off.}
when defined(nimKnowsNimvm): when defined(nimKnowsNimvm):
@ -1435,7 +1440,12 @@ when defined(nimdoc):
elif defined(genode): elif defined(genode):
proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn, 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: else:
proc quit*(errorcode: int = QuitSuccess) {. proc quit*(errorcode: int = QuitSuccess) {.
@ -3719,7 +3729,9 @@ proc shallow*(s: var string) {.noSideEffect, inline.} =
## purposes. ## purposes.
when not defined(JS) and not defined(nimscript): when not defined(JS) and not defined(nimscript):
var s = cast[PGenericSeq](s) 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 type
NimNodeObj = object NimNodeObj = object

View file

@ -63,12 +63,17 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
sysAssert(dest != nil, "genericAssignAux 3") sysAssert(dest != nil, "genericAssignAux 3")
unsureAsgnRef(x, newSeq(mt, seq.len)) unsureAsgnRef(x, newSeq(mt, seq.len))
var dst = cast[ByteAddress](cast[PPointer](dest)[]) var dst = cast[ByteAddress](cast[PPointer](dest)[])
for i in 0..seq.len-1: if ntfNoRefs in mt.base.flags:
genericAssignAux( copyMem(cast[pointer](dst +% GenericSeqSize),
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize), cast[pointer](cast[ByteAddress](s2) +% GenericSeqSize),
cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +% seq.len * mt.base.size)
GenericSeqSize), else:
mt.base, shallow) 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: of tyObject:
if mt.base != nil: if mt.base != nil:
genericAssignAux(dest, src, mt.base, shallow) 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) cast[pointer](s +% i*% mt.base.size), mt.base, shallow)
of tyRef: of tyRef:
unsureAsgnRef(cast[PPointer](dest), cast[PPointer](s)[]) 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: else:
copyMem(dest, src, mt.size) # copy raw bits copyMem(dest, src, mt.size) # copy raw bits
@ -115,6 +133,7 @@ when false:
of tyPtr: k = "ptr" of tyPtr: k = "ptr"
of tyRef: k = "ref" of tyRef: k = "ref"
of tyVar: k = "var" of tyVar: k = "var"
of tyOptAsRef: k = "optref"
of tySequence: k = "seq" of tySequence: k = "seq"
of tyProc: k = "proc" of tyProc: k = "proc"
of tyPointer: k = "range" of tyPointer: k = "range"
@ -195,7 +214,7 @@ proc genericReset(dest: pointer, mt: PNimType) =
var d = cast[ByteAddress](dest) var d = cast[ByteAddress](dest)
sysAssert(mt != nil, "genericReset 2") sysAssert(mt != nil, "genericReset 2")
case mt.kind case mt.kind
of tyString, tyRef, tySequence: of tyString, tyRef, tyOptAsRef, tySequence:
unsureAsgnRef(cast[PPointer](dest), nil) unsureAsgnRef(cast[PPointer](dest), nil)
of tyTuple: of tyTuple:
genericResetAux(dest, mt.node) 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: for i in 0..(mt.size div mt.base.size)-1:
storeAux(cast[pointer](d +% i*% mt.base.size), storeAux(cast[pointer](d +% i*% mt.base.size),
cast[pointer](s +% i*% mt.base.size), mt.base, t, mode) cast[pointer](s +% i*% mt.base.size), mt.base, t, mode)
of tyRef: of tyRef, tyOptAsRef:
var s = cast[PPointer](src)[] var s = cast[PPointer](src)[]
var x = cast[PPointer](dest) var x = cast[PPointer](dest)
if s == nil: 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: for i in 0..(mt.size div mt.base.size)-1:
genericDeepCopyAux(cast[pointer](d +% i*% mt.base.size), genericDeepCopyAux(cast[pointer](d +% i*% mt.base.size),
cast[pointer](s +% i*% mt.base.size), mt.base, tab) cast[pointer](s +% i*% mt.base.size), mt.base, tab)
of tyRef: of tyRef, tyOptAsRef:
let s2 = cast[PPointer](src)[] let s2 = cast[PPointer](src)[]
if s2 == nil: if s2 == nil:
unsureAsgnRef(cast[PPointer](dest), s2) 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: for i in 0..n.len-1:
# inlined for speed # inlined for speed
if n.sons[i].kind == nkSlot: 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) doOperation(cast[PPointer](d +% n.sons[i].offset)[], op)
else: else:
forAllChildrenAux(cast[pointer](d +% n.sons[i].offset), 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 dest == nil: return # nothing to do
if ntfNoRefs notin mt.flags: if ntfNoRefs notin mt.flags:
case mt.kind case mt.kind
of tyRef, tyString, tySequence: # leaf: of tyRef, tyOptAsRef, tyString, tySequence: # leaf:
doOperation(cast[PPointer](d)[], op) doOperation(cast[PPointer](d)[], op)
of tyObject, tyTuple: of tyObject, tyTuple:
forAllSlotsAux(dest, mt.node, op) forAllSlotsAux(dest, mt.node, op)
@ -379,13 +379,13 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
gcAssert(cell != nil, "forAllChildren: 1") gcAssert(cell != nil, "forAllChildren: 1")
gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2") gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2")
gcAssert(cell.typ != nil, "forAllChildren: 3") 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 let marker = cell.typ.marker
if marker != nil: if marker != nil:
marker(cellToUsr(cell), op.int) marker(cellToUsr(cell), op.int)
else: else:
case cell.typ.kind case cell.typ.kind
of tyRef: # common case of tyRef, tyOptAsRef: # common case
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op) forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
of tySequence: of tySequence:
var d = cast[ByteAddress](cellToUsr(cell)) var d = cast[ByteAddress](cellToUsr(cell))
@ -461,7 +461,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
incTypeSize typ, size incTypeSize typ, size
sysAssert(allocInv(gch.region), "rawNewObj begin") sysAssert(allocInv(gch.region), "rawNewObj begin")
acquire(gch) acquire(gch)
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
collectCT(gch) collectCT(gch)
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
#gcAssert typ.kind in {tyString, tySequence} or size >= typ.base.size, "size too small" #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 incTypeSize typ, size
sysAssert(allocInv(gch.region), "newObjRC1 begin") sysAssert(allocInv(gch.region), "newObjRC1 begin")
acquire(gch) acquire(gch)
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
collectCT(gch) collectCT(gch)
sysAssert(allocInv(gch.region), "newObjRC1 after collectCT") 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 cycle table size: " & $gch.stat.cycleTableSize & "\n" &
"[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000) & "\n" "[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000) & "\n"
when nimCoroutines: 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): 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: 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 {.pop.} # profiler: off, stackTrace: off

View file

@ -1,7 +1,7 @@
# #
# #
# Nim's Runtime Library # Nim's Runtime Library
# (c) Copyright 2015 Andreas Rumpf # (c) Copyright 2017 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -9,17 +9,19 @@
# Garbage Collector # 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 # 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. # according to its rules, no deadline will ever be missed.
# Since this kind of collector is very bad at recycling dead objects
# XXX Ensure by smart color masking that the object is not in the ZCT. # 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.} {.push profiler:off.}
const const
CycleIncrease = 2 # is a multiplicative increase 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 ZctThreshold = 500 # we collect garbage if the ZCT's size
# reaches this threshold # reaches this threshold
# this seems to be a good value # this seems to be a good value
@ -40,13 +42,11 @@ type
iterToProc(allObjects, ptr ObjectSpaceIter, allObjectsAsProc) iterToProc(allObjects, ptr ObjectSpaceIter, allObjectsAsProc)
const const
rcIncrement = 0b1000 # so that lowest 3 bits are not touched escapedBit = 0b1000 # so that lowest 3 bits are not touched
rcBlackOrig = 0b000 rcBlackOrig = 0b000
rcWhiteOrig = 0b001 rcWhiteOrig = 0b001
rcGrey = 0b010 # traditional color for incremental mark&sweep rcGrey = 0b010 # traditional color for incremental mark&sweep
rcUnused = 0b011 rcUnused = 0b011
ZctFlag = 0b100 # in ZCT
rcShift = 3 # shift by rcShift to get the reference counter
colorMask = 0b011 colorMask = 0b011
type type
WalkOp = enum WalkOp = enum
@ -63,7 +63,7 @@ type
GcStat = object GcStat = object
stackScans: int # number of performed stack scans (for statistics) 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 maxThreshold: int # max threshold that has been set
maxStackSize: int # max stack size maxStackSize: int # max stack size
maxStackCells: int # max stack cells in ``decStack`` maxStackCells: int # max stack cells in ``decStack``
@ -93,15 +93,13 @@ type
cycleThreshold: int cycleThreshold: int
when useCellIds: when useCellIds:
idGenerator: int idGenerator: int
zct: CellSeq # the zero count table
decStack: CellSeq # cells in the stack that are to decref again
greyStack: CellSeq greyStack: CellSeq
recGcLock: int # prevent recursion via finalizers; no thread lock recGcLock: int # prevent recursion via finalizers; no thread lock
when withRealTime: when withRealTime:
maxPause: Nanos # max allowed pause in nanoseconds; active if > 0 maxPause: Nanos # max allowed pause in nanoseconds; active if > 0
region: MemRegion # garbage collected region region: MemRegion # garbage collected region
stat: GcStat stat: GcStat
additionalRoots: CellSeq # dummy roots for GC_ref/unref additionalRoots: CellSeq # explicit roots for GC_ref/unref
spaceIter: ObjectSpaceIter spaceIter: ObjectSpaceIter
pDumpHeapFile: pointer # File that is used for GC_dumpHeap pDumpHeapFile: pointer # File that is used for GC_dumpHeap
when hasThreadSupport: when hasThreadSupport:
@ -113,19 +111,25 @@ var
when not defined(useNimRtl): when not defined(useNimRtl):
instantiateForRegion(gch.region) 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() = proc initGC() =
when not defined(useNimRtl): when not defined(useNimRtl):
gch.red = (1-gch.black) gch.red = (1-gch.black)
gch.cycleThreshold = InitialCycleThreshold gch.cycleThreshold = InitialCycleThreshold
gch.stat.stackScans = 0 gch.stat.stackScans = 0
gch.stat.cycleCollections = 0 gch.stat.completedCollections = 0
gch.stat.maxThreshold = 0 gch.stat.maxThreshold = 0
gch.stat.maxStackSize = 0 gch.stat.maxStackSize = 0
gch.stat.maxStackCells = 0 gch.stat.maxStackCells = 0
gch.stat.cycleTableSize = 0 gch.stat.cycleTableSize = 0
# init the rt # init the rt
init(gch.zct)
init(gch.decStack)
init(gch.additionalRoots) init(gch.additionalRoots)
init(gch.greyStack) init(gch.greyStack)
when hasThreadSupport: when hasThreadSupport:
@ -147,11 +151,6 @@ template gcAssert(cond: bool, msg: string) =
writeStackTrace() writeStackTrace()
quit 1 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.} = proc cellToUsr(cell: PCell): pointer {.inline.} =
# convert object (=pointer to refcount) to pointer to userdata # convert object (=pointer to refcount) to pointer to userdata
result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell))) result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell)))
@ -168,7 +167,7 @@ proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
result = usrToCell(c).typ result = usrToCell(c).typ
proc internRefcount(p: pointer): int {.exportc: "getRefcount".} = 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: # this that has to equals zero, otherwise we have to round up UnitsPerPage:
when BitsPerPage mod (sizeof(int)*8) != 0: when BitsPerPage mod (sizeof(int)*8) != 0:
@ -178,6 +177,12 @@ template color(c): expr = c.refCount and colorMask
template setColor(c, col) = template setColor(c, col) =
c.refcount = c.refcount and not colorMask or 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) = proc writeCell(file: File; msg: cstring, c: PCell) =
var kind = -1 var kind = -1
if c.typ != nil: kind = ord(c.typ.kind) if c.typ != nil: kind = ord(c.typ.kind)
@ -189,18 +194,18 @@ proc writeCell(file: File; msg: cstring, c: PCell) =
else: else:
let id = c let id = c
when leakDetector: when leakDetector:
c_fprintf(file, "%s %p %d rc=%ld color=%c from %s(%ld)\n", c_fprintf(file, "%s %p %d escaped=%ld color=%c from %s(%ld)\n",
msg, id, kind, c.refcount shr rcShift, col, c.filename, c.line) msg, id, kind, didEscape(c), col, c.filename, c.line)
else: else:
c_fprintf(file, "%s %p %d rc=%ld color=%c\n", c_fprintf(file, "%s %p %d escaped=%ld color=%c\n",
msg, id, kind, c.refcount shr rcShift, col) msg, id, kind, didEscape(c), col)
proc writeCell(msg: cstring, c: PCell) = proc writeCell(msg: cstring, c: PCell) =
stdout.writeCell(msg, c) stdout.writeCell(msg, c)
proc myastToStr[T](x: T): string {.magic: "AstToStr", noSideEffect.} 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) when traceGC: writeCell(myastToStr(state), cell)
# forward declarations: # forward declarations:
@ -211,52 +216,17 @@ proc doOperation(p: pointer, op: WalkOp) {.benign.}
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.} proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
# we need the prototype here for debugging purposes # 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.} = proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
# we MUST access gch as a global here, because this crosses DLL boundaries! # we MUST access gch as a global here, because this crosses DLL boundaries!
discard 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.} = proc nimGCref(p: pointer) {.compilerProc.} =
let cell = usrToCell(p) let cell = usrToCell(p)
incRef(cell) markAsEscaped(cell)
add(gch.additionalRoots, cell) add(gch.additionalRoots, cell)
proc nimGCunref(p: pointer) {.compilerProc.} = proc nimGCunref(p: pointer) {.compilerProc.} =
let cell = usrToCell(p) let cell = usrToCell(p)
decRef(cell)
var L = gch.additionalRoots.len-1 var L = gch.additionalRoots.len-1
var i = L var i = L
let d = gch.additionalRoots.d let d = gch.additionalRoots.d
@ -267,6 +237,12 @@ proc nimGCunref(p: pointer) {.compilerProc.} =
break break
dec(i) 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) = template markGrey(x: PCell) =
if x.color != 1-gch.black and gch.phase == Phase.Marking: if x.color != 1-gch.black and gch.phase == Phase.Marking:
if not isAllocatedPtr(gch.region, x): 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 ## adds 'p' to the cycle candidate set for the cycle collector. It is
## necessary if you used the 'acyclic' pragma for optimization ## necessary if you used the 'acyclic' pragma for optimization
## purposes and need to break cycles manually. ## purposes and need to break cycles manually.
rtlAddCycleRoot(usrToCell(cast[pointer](p))) discard
proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} = template asgnRefImpl =
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!
gcAssert(not isOnStack(dest), "asgnRef") gcAssert(not isOnStack(dest), "asgnRef")
# BUGFIX: first incRef then decRef! # BUGFIX: first incRef then decRef!
if src != nil: if src != nil:
let s = usrToCell(src) let s = usrToCell(src)
incRef(s) markAsEscaped(s)
markGrey(s) markGrey(s)
if dest[] != nil: decRef(usrToCell(dest[]))
dest[] = src 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.} = proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} =
# the code generator calls this proc if it is known at compile time that no asgnRefImpl()
# 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
proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} = 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 # 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). # reference is in the stack or not (this can happen for var parameters).
if not isOnStack(dest): if src != nil:
if src != nil: let s = usrToCell(src)
let s = usrToCell(src) markAsEscaped(s)
incRef(s) if not isOnStack(dest): markGrey(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")
dest[] = src dest[] = src
type type
@ -366,7 +315,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
if dest == nil: return # nothing to do if dest == nil: return # nothing to do
if ntfNoRefs notin mt.flags: if ntfNoRefs notin mt.flags:
case mt.kind case mt.kind
of tyRef, tyString, tySequence: # leaf: of tyRef, tyOptAsRef, tyString, tySequence: # leaf:
doOperation(cast[PPointer](d)[], op) doOperation(cast[PPointer](d)[], op)
of tyObject, tyTuple: of tyObject, tyTuple:
forAllSlotsAux(dest, mt.node, op) forAllSlotsAux(dest, mt.node, op)
@ -379,13 +328,13 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
gcAssert(cell != nil, "forAllChildren: 1") gcAssert(cell != nil, "forAllChildren: 1")
gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2") gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2")
gcAssert(cell.typ != nil, "forAllChildren: 3") 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 let marker = cell.typ.marker
if marker != nil: if marker != nil:
marker(cellToUsr(cell), op.int) marker(cellToUsr(cell), op.int)
else: else:
case cell.typ.kind case cell.typ.kind
of tyRef: # common case of tyRef, tyOptAsRef: # common case
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op) forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
of tySequence: of tySequence:
var d = cast[ByteAddress](cellToUsr(cell)) var d = cast[ByteAddress](cellToUsr(cell))
@ -396,50 +345,6 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
GenericSeqSize), cell.typ.base, op) GenericSeqSize), cell.typ.base, op)
else: discard 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.} {.push stackTrace: off, profiler:off.}
proc gcInvariant*() = proc gcInvariant*() =
sysAssert(allocInv(gch.region), "injected") sysAssert(allocInv(gch.region), "injected")
@ -447,10 +352,12 @@ proc gcInvariant*() =
markForDebug(gch) markForDebug(gch)
{.pop.} {.pop.}
include gc_common
proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
# generates a new object and sets its reference counter to 0 # generates a new object and sets its reference counter to 0
sysAssert(allocInv(gch.region), "rawNewObj begin") 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) collectCT(gch)
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") 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.filename = framePtr.prev.filename
res.line = framePtr.prev.line res.line = framePtr.prev.line
# refcount is zero, color is black, but mark it to be in the ZCT # 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") 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) when logGC: writeCell("new cell", res)
gcTrace(res, csAllocated) gcTrace(res, csAllocated)
when useCellIds: when useCellIds:
@ -493,95 +398,38 @@ proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
when defined(memProfiler): nimProfile(size) when defined(memProfiler): nimProfile(size)
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
# generates a new object and sets its reference counter to 1 result = newObj(typ, size)
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)
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} = proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) result = newSeq(typ, len)
result = newObjRC1(typ, size)
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len
when defined(memProfiler): nimProfile(size)
proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
acquire(gch)
collectCT(gch) collectCT(gch)
var ol = usrToCell(old) var ol = usrToCell(old)
gcAssert(isAllocatedPtr(gch.region, ol), "growObj: freed pointer?")
sysAssert(ol.typ != nil, "growObj: 1") sysAssert(ol.typ != nil, "growObj: 1")
gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2") 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 res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
var elemSize = 1 var elemSize = 1
if ol.typ.kind != tyString: elemSize = ol.typ.base.size 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)) 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) newsize-oldsize)
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
# This can be wrong for intermediate temps that are nevertheless on the when false:
# heap because of lambda lifting: # this is wrong since seqs can be shared via 'shallow':
#gcAssert(res.refcount shr rcShift <=% 1, "growObj: 4") when reallyDealloc: rawDealloc(gch.region, ol)
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)
else: else:
# we split the old refcount in 2 parts. XXX This is still not entirely zeroMem(ol, sizeof(Cell))
# 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))
when useCellIds: when useCellIds:
inc gch.idGenerator inc gch.idGenerator
res.id = gch.idGenerator res.id = gch.idGenerator
release(gch)
result = cellToUsr(res) result = cellToUsr(res)
sysAssert(allocInv(gch.region), "growObj end")
when defined(memProfiler): nimProfile(newsize-oldsize) when defined(memProfiler): nimProfile(newsize-oldsize)
proc growObj(old: pointer, newsize: int): pointer {.rtl.} = 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. ## can be translated into "dot" syntax via the "heapdump2dot" tool.
gch.pDumpHeapFile = file gch.pDumpHeapFile = file
var spaceIter: ObjectSpaceIter var spaceIter: ObjectSpaceIter
var d = gch.decStack.d when false:
for i in 0 .. < gch.decStack.len: var d = gch.decStack.d
if isAllocatedPtr(gch.region, d[i]): for i in 0 .. < gch.decStack.len:
c_fprintf(file, "onstack %p\n", d[i]) if isAllocatedPtr(gch.region, d[i]):
else: c_fprintf(file, "onstack %p\n", d[i])
c_fprintf(file, "onstack_invalid %p\n", d[i]) else:
c_fprintf(file, "onstack_invalid %p\n", d[i])
for i in 0 .. < globalMarkersLen: globalMarkers[i]() for i in 0 .. < globalMarkersLen: globalMarkers[i]()
while true: while true:
let x = allObjectsAsProc(gch.region, addr spaceIter) let x = allObjectsAsProc(gch.region, addr spaceIter)
@ -667,14 +516,6 @@ proc GC_dumpHeap() =
proc freeCyclicCell(gch: var GcHeap, c: PCell) = proc freeCyclicCell(gch: var GcHeap, c: PCell) =
gcAssert(isAllocatedPtr(gch.region, c), "freeCyclicCell: freed pointer?") 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) prepareDealloc(c)
gcTrace(c, csCycFreed) gcTrace(c, csCycFreed)
when logGC: writeCell("cycle collector dealloc cell", c) 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: if c.color == 1-gch.black:
c.setColor(rcGrey) c.setColor(rcGrey)
add(gch.greyStack, c) 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 = proc markIncremental(gch: var GcHeap): bool =
var L = addr(gch.greyStack.len) var L = addr(gch.greyStack.len)
@ -741,30 +573,14 @@ proc markIncremental(gch: var GcHeap): bool =
c.setColor(gch.black) c.setColor(gch.black)
forAllChildren(c, waMarkGrey) forAllChildren(c, waMarkGrey)
elif c.color == (1-gch.black): 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() checkTime()
gcAssert gch.greyStack.len == 0, "markIncremental: greystack not empty " 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 result = true
proc markGlobals(gch: var GcHeap) = proc markGlobals(gch: var GcHeap) =
for i in 0 .. < globalMarkersLen: globalMarkers[i]() 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) = proc doOperation(p: pointer, op: WalkOp) =
if p == nil: return if p == nil: return
var c: PCell = usrToCell(p) var c: PCell = usrToCell(p)
@ -776,11 +592,7 @@ proc doOperation(p: pointer, op: WalkOp) =
#if not isAllocatedPtr(gch.region, c): #if not isAllocatedPtr(gch.region, c):
# c_fprintf(stdout, "[GC] decref bug: %p", c) # c_fprintf(stdout, "[GC] decref bug: %p", c)
gcAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef") gcAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef")
gcAssert(c.refcount >=% rcIncrement, "doOperation 2") discard "use me for nimEscape?"
#c.refcount = c.refcount -% rcIncrement
when logGC: writeCell("decref (from doOperation)", c)
decRef(c)
#if c.refcount <% rcIncrement: addZCT(gch.zct, c)
of waMarkGlobal: of waMarkGlobal:
template handleRoot = template handleRoot =
if gch.dumpHeapFile.isNil: if gch.dumpHeapFile.isNil:
@ -811,40 +623,6 @@ proc doOperation(p: pointer, op: WalkOp) =
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
doOperation(d, WalkOp(op)) 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.} = proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
# the addresses are not as cells on the stack, so turn them to cells: # the addresses are not as cells on the stack, so turn them to cells:
sysAssert(allocInv(gch.region), "gcMark begin") 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)) var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell))
if objStart != nil: if objStart != nil:
# mark the cell: # mark the cell:
objStart.refcount = objStart.refcount +% rcIncrement markRoot(gch, objStart)
add(gch.decStack, objStart)
sysAssert(allocInv(gch.region), "gcMark end") sysAssert(allocInv(gch.region), "gcMark end")
include gc_common
proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} = proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
forEachStackSlot(gch, gcMark) forEachStackSlot(gch, gcMark)
proc collectZCT(gch: var GcHeap): bool = proc collectALittle(gch: var GcHeap): bool =
# Note: Freeing may add child objects to the ZCT! So essentially we do case gch.phase
# deep freeing, which is bad for incremental operation. In order to of Phase.None:
# avoid a deep stack, we move objects to keep the ZCT small. if getOccupiedMem(gch.region) >= gch.cycleThreshold:
# This is performance critical! gch.phase = Phase.Marking
var L = addr(gch.zct.len) markGlobals(gch)
takeStartTime(100) result = collectALittle(gch)
#when false: c_fprintf(stdout, "collectALittle: introduced bug E %ld\n", gch.phase)
while L[] > 0: #discard allocInv(gch.region)
var c = gch.zct.d[0] of Phase.Marking:
sysAssert(isAllocatedPtr(gch.region, c), "CollectZCT: isAllocatedPtr") when hasThreadSupport:
# remove from ZCT: for c in gch.toDispose:
gcAssert((c.refcount and ZctFlag) == ZctFlag, "collectZCT") nimGCunref(c)
prepareForInteriorPointerChecking(gch.region)
c.refcount = c.refcount and not ZctFlag markStackAndRegisters(gch)
gch.zct.d[0] = gch.zct.d[L[] - 1] inc(gch.stat.stackScans)
dec(L[]) if markIncremental(gch):
takeTime() gch.phase = Phase.Sweeping
if c.refcount <% rcIncrement and c.color != rcGrey: gch.red = 1 - gch.red
# It may have a RC > 0, if it is in the hardware stack or of Phase.Sweeping:
# it has not been removed yet from the ZCT. This is because gcAssert gch.greyStack.len == 0, "greystack not empty"
# ``incref`` does not bother to remove the cell from the ZCT when hasThreadSupport:
# as this might be too slow. for c in gch.toDispose:
# In any case, it should be removed from the ZCT. But not nimGCunref(c)
# freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!** if sweep(gch):
when logGC: writeCell("zct dealloc cell", c) gch.phase = Phase.None
gcTrace(c, csZctFreed) # flip black/white meanings:
# We are about to free the object, call the finalizer BEFORE its gch.black = 1 - gch.black
# children are deleted as well, because otherwise the finalizer may gcAssert gch.red == 1 - gch.black, "red color is wrong"
# access invalid memory. This is done by prepareDealloc(): inc(gch.stat.completedCollections)
prepareDealloc(c) result = true
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 collectCTBody(gch: var GcHeap) = proc collectCTBody(gch: var GcHeap) =
when withRealTime: when withRealTime:
@ -919,22 +678,12 @@ proc collectCTBody(gch: var GcHeap) =
when not nimCoroutines: when not nimCoroutines:
gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize()) gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
sysAssert(gch.decStack.len == 0, "collectCT") #gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len)
prepareForInteriorPointerChecking(gch.region) if collectALittle(gch):
markStackAndRegisters(gch) gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len) CycleIncrease)
inc(gch.stat.stackScans) gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
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)
sysAssert(allocInv(gch.region), "collectCT: end") sysAssert(allocInv(gch.region), "collectCT: end")
when withRealTime: when withRealTime:
let duration = getticks() - t0 let duration = getticks() - t0
gch.stat.maxPause = max(gch.stat.maxPause, duration) 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) let stackMarkCosts = max(currentStackSizes() div (16*sizeof(int)), ZctThreshold)
else: else:
let stackMarkCosts = max(stackSize() div (16*sizeof(int)), ZctThreshold) 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 getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
gch.recGcLock == 0: gch.recGcLock == 0:
collectCTBody(gch) collectCTBody(gch)
@ -969,10 +718,9 @@ when withRealTime:
proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) = proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) =
gch.maxPause = us.toNano gch.maxPause = us.toNano
if (gch.zct.len >= ZctThreshold or (cycleGC and #if (getOccupiedMem(gch.region)>=gch.cycleThreshold) or
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or # alwaysGC or strongAdvice:
strongAdvice: collectCTBody(gch)
collectCTBody(gch)
proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} = proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} =
if stackSize >= 0: if stackSize >= 0:
@ -1010,12 +758,8 @@ when not defined(useNimRtl):
proc GC_setStrategy(strategy: GC_Strategy) = proc GC_setStrategy(strategy: GC_Strategy) =
discard discard
proc GC_enableMarkAndSweep() = proc GC_enableMarkAndSweep() = discard
gch.cycleThreshold = InitialCycleThreshold proc GC_disableMarkAndSweep() = discard
proc GC_disableMarkAndSweep() =
gch.cycleThreshold = high(gch.cycleThreshold)-1
# set to the max value to suppress the cycle detector
proc GC_fullCollect() = proc GC_fullCollect() =
var oldThreshold = gch.cycleThreshold var oldThreshold = gch.cycleThreshold
@ -1029,17 +773,17 @@ when not defined(useNimRtl):
"[GC] occupied memory: " & $(getOccupiedMem()) & "\n" & "[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
"[GC] stack scans: " & $gch.stat.stackScans & "\n" & "[GC] stack scans: " & $gch.stat.stackScans & "\n" &
"[GC] stack cells: " & $gch.stat.maxStackCells & "\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] 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 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: 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): 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: else:
result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n" result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
GC_enable() GC_enable()
{.pop.} {.pop.}

View file

@ -373,12 +373,22 @@ proc deallocHeap*(runFinalizers = true; allowGcAfterwards = true) =
## is true. If ``allowGcAfterwards`` is true, a minimal amount of allocation ## is true. If ``allowGcAfterwards`` is true, a minimal amount of allocation
## happens to ensure the GC can continue to work after the call ## happens to ensure the GC can continue to work after the call
## to ``deallocHeap``. ## to ``deallocHeap``.
template deallocCell(x) =
if isCell(x):
# cast to PCell is correct here:
prepareDealloc(cast[PCell](x))
if runFinalizers: if runFinalizers:
for x in allObjects(gch.region): when not declared(allObjectsAsProc):
if isCell(x): for x in allObjects(gch.region):
# cast to PCell is correct here: deallocCell(x)
var c = cast[PCell](x) else:
prepareDealloc(c) var spaceIter: ObjectSpaceIter
while true:
let x = allObjectsAsProc(gch.region, addr spaceIter)
if spaceIter.state < 0: break
deallocCell(x)
deallocOsPages(gch.region) deallocOsPages(gch.region)
zeroMem(addr gch.region, sizeof(gch.region)) zeroMem(addr gch.region, sizeof(gch.region))
if allowGcAfterwards: if allowGcAfterwards:

View file

@ -252,7 +252,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
if dest == nil: return # nothing to do if dest == nil: return # nothing to do
if ntfNoRefs notin mt.flags: if ntfNoRefs notin mt.flags:
case mt.kind case mt.kind
of tyRef, tyString, tySequence: # leaf: of tyRef, tyOptAsRef, tyString, tySequence: # leaf:
doOperation(cast[PPointer](d)[], op) doOperation(cast[PPointer](d)[], op)
of tyObject, tyTuple: of tyObject, tyTuple:
forAllSlotsAux(dest, mt.node, op) forAllSlotsAux(dest, mt.node, op)
@ -264,13 +264,13 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
proc forAllChildren(cell: PCell, op: WalkOp) = proc forAllChildren(cell: PCell, op: WalkOp) =
gcAssert(cell != nil, "forAllChildren: 1") gcAssert(cell != nil, "forAllChildren: 1")
gcAssert(cell.typ != nil, "forAllChildren: 2") 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 let marker = cell.typ.marker
if marker != nil: if marker != nil:
marker(cellToUsr(cell), op.int) marker(cellToUsr(cell), op.int)
else: else:
case cell.typ.kind case cell.typ.kind
of tyRef: # common case of tyRef, tyOptAsRef: # common case
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op) forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
of tySequence: of tySequence:
var d = cast[ByteAddress](cellToUsr(cell)) 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 # generates a new object and sets its reference counter to 0
incTypeSize typ, size incTypeSize typ, size
acquire(gch) acquire(gch)
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
collectCT(gch) collectCT(gch)
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") 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] max threshold: " & $gch.stat.maxThreshold & "\n" &
"[GC] freed objects: " & $gch.stat.freedObjects & "\n" "[GC] freed objects: " & $gch.stat.freedObjects & "\n"
when nimCoroutines: 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): 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: else:
result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n" result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
{.pop.} {.pop.}

View file

@ -62,6 +62,21 @@ type
tyUInt16, tyUInt16,
tyUInt32, tyUInt32,
tyUInt64, 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 TNimNodeKind = enum nkNone, nkSlot, nkList, nkCase
TNimNode {.codegenType.} = object TNimNode {.codegenType.} = object

View file

@ -564,7 +564,11 @@ else:
when not declared(nimNewSeqOfCap): when not declared(nimNewSeqOfCap):
proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} = 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).len = 0
cast[PGenericSeq](result).reserved = cap cast[PGenericSeq](result).reserved = cap

View file

@ -11,6 +11,15 @@
# Nim's configuration system now uses Nim for scripting. This module provides # Nim's configuration system now uses Nim for scripting. This module provides
# a few things that are required for this to work. # 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 template builtin = discard
# We know the effects better than the compiler: # 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) if str == nil: NimString(nil)
else: toNimStr(str, str.len) else: toNimStr(str, str.len)
template wasMoved(x: NimString): bool = false
# (x.reserved and seqShallowFlag) != 0
proc copyString(src: NimString): NimString {.compilerRtl.} = proc copyString(src: NimString): NimString {.compilerRtl.} =
if src != nil: if src != nil:
if (src.reserved and seqShallowFlag) != 0: if (src.reserved and seqShallowFlag) != 0:
@ -103,6 +106,16 @@ proc copyString(src: NimString): NimString {.compilerRtl.} =
result = rawNewStringNoInit(src.len) result = rawNewStringNoInit(src.len)
result.len = src.len result.len = src.len
copyMem(addr(result.data), addr(src.data), src.len + 1) 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.} = proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
if src != nil: if src != nil:
@ -116,6 +129,10 @@ proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
result = rawNewStringNoInit(src.len) result = rawNewStringNoInit(src.len)
result.len = src.len result.len = src.len
copyMem(addr(result.data), addr(src.data), src.len + 1) 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.} = proc copyDeepString(src: NimString): NimString {.inline.} =
if src != nil: if src != nil:
@ -140,9 +157,12 @@ proc addChar(s: NimString, c: char): NimString =
# is compilerproc! # is compilerproc!
result = s result = s
if result.len >= result.space: if result.len >= result.space:
result.reserved = resize(result.space) let r = resize(result.space)
result = cast[NimString](growObj(result, 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] = c
result.data[result.len+1] = '\0' result.data[result.len+1] = '\0'
inc(result.len) inc(result.len)
@ -179,7 +199,7 @@ proc addChar(s: NimString, c: char): NimString =
# s = rawNewString(0); # s = rawNewString(0);
proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} = 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 result = dest
else: # slow path: else: # slow path:
var sp = max(resize(dest.space), dest.len + addlen) 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.} = proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
var n = max(newLen, 0) var n = max(newLen, 0)
if n <= s.space: if wasMoved(s):
result = newOwnedString(s, n)
elif n <= s.space:
result = s result = s
else: else:
result = resizeString(s, n) result = resizeString(s, n)
@ -218,26 +240,29 @@ proc incrSeq(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
# seq[seq->len-1] = x; # seq[seq->len-1] = x;
result = seq result = seq
if result.len >= result.space: if result.len >= result.space:
result.reserved = resize(result.space) let r = resize(result.space)
result = cast[PGenericSeq](growObj(result, elemSize * result.reserved + result = cast[PGenericSeq](growObj(result, elemSize * r +
GenericSeqSize)) GenericSeqSize))
result.reserved = r
inc(result.len) inc(result.len)
proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} = proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
# incrSeq version 2 # incrSeq version 2
result = seq result = seq
if result.len >= result.space: if result.len >= result.space:
result.reserved = resize(result.space) let r = resize(result.space)
result = cast[PGenericSeq](growObj(result, elemSize * result.reserved + result = cast[PGenericSeq](growObj(result, elemSize * r +
GenericSeqSize)) GenericSeqSize))
result.reserved = r
proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {. proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
compilerRtl.} = compilerRtl.} =
result = seq result = seq
if result.space < newLen: if result.space < newLen:
result.reserved = max(resize(result.space), newLen) let r = max(resize(result.space), newLen)
result = cast[PGenericSeq](growObj(result, elemSize * result.reserved + result = cast[PGenericSeq](growObj(result, elemSize * r +
GenericSeqSize)) GenericSeqSize))
result.reserved = r
elif newLen < result.len: elif newLen < result.len:
# we need to decref here, otherwise the GC leaks! # we need to decref here, otherwise the GC leaks!
when not defined(boehmGC) and not defined(nogc) and when not defined(boehmGC) and not defined(nogc) and

View file

@ -127,7 +127,8 @@ elif defined(genode):
proc initThread(s: var SysThread, proc initThread(s: var SysThread,
stackSize: culonglong, stackSize: culonglong,
entry: GenodeThreadProc, entry: GenodeThreadProc,
arg: pointer) {. arg: pointer,
affinity: cuint) {.
importcpp: "#.initThread(genodeEnv, @)".} importcpp: "#.initThread(genodeEnv, @)".}
proc threadVarAlloc(): ThreadVarSlot = 0 proc threadVarAlloc(): ThreadVarSlot = 0
@ -567,6 +568,9 @@ when hostOS == "windows":
setThreadAffinityMask(t.sys, uint(1 shl cpu)) setThreadAffinityMask(t.sys, uint(1 shl cpu))
elif defined(genode): elif defined(genode):
var affinityOffset: cuint = 1
# CPU affinity offset for next thread, safe to roll-over
proc createThread*[TArg](t: var Thread[TArg], proc createThread*[TArg](t: var Thread[TArg],
tp: proc (arg: TArg) {.thread, nimcall.}, tp: proc (arg: TArg) {.thread, nimcall.},
param: TArg) = param: TArg) =
@ -577,7 +581,8 @@ elif defined(genode):
when hasSharedHeap: t.stackSize = ThreadStackSize when hasSharedHeap: t.stackSize = ThreadStackSize
t.sys.initThread( t.sys.initThread(
ThreadStackSize.culonglong, ThreadStackSize.culonglong,
threadProcWrapper[TArg], addr(t)) threadProcWrapper[TArg], addr(t), affinityOffset)
inc affinityOffset
proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) = proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) =
{.hint: "cannot change Genode thread CPU affinity after initialization".} {.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 enumA, enumB, enumC, enumD, enumE, enumLast
proc vm() = proc vm() =
var instructions: array [0..100, MyEnum] var instructions: array[0..100, MyEnum]
instructions[2] = enumC instructions[2] = enumC
instructions[3] = enumD instructions[3] = enumD
instructions[4] = enumA instructions[4] = enumA

View file

@ -19,7 +19,7 @@ proc nrow*(dt: DataTable) : Natural =
return totalLen return totalLen
let 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) echo nrow(stud)

View file

@ -14,13 +14,13 @@ type
proc testSem = proc testSem =
var 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: 0.0, chars: {'a', 'b', 'c'}),
(s: "hi", x: 69, y: 45, z: 1.0, chars: {'a', 'b', 'c'})] (s: "hi", x: 69, y: 45, z: 1.0, chars: {'a', 'b', 'c'})]
write(stdout, things[0].x) write(stdout, things[0].x)
const 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: 0.0, chars: {'a', 'b', 'c'}),
(s: "hi", x: 69, y: 45, z: 1.0)] #ERROR (s: "hi", x: 69, y: 45, z: 1.0)] #ERROR
otherThings = [ # the same otherThings = [ # the same

View file

@ -12,7 +12,7 @@ type
chars: set[char]] chars: set[char]]
const 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: 0.0, chars: {'a', 'b', 'c'}),
(s: "hi", x: 69, y: 45, z: 1.0, chars: {})] (s: "hi", x: 69, y: 45, z: 1.0, chars: {})]
otherThings = [ # the same otherThings = [ # the same

View file

@ -23,29 +23,29 @@ asm """
""" """
proc consoleprint (str:cstring): void {.importc: "print", noDecl.} 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 type CallbackProc {.importc.} = proc () : cstring
proc regCallback (fn:CallbackProc) {.importc.} proc regCallback (fn:CallbackProc) {.importc.}
proc runCallbacks ():cstring {.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) = proc outer (i:Natural) : (string, int) =
let c = $char (random (93) + 33) let c = $char(random(93) + 33)
let n = random (40) let n = random(40)
let s = c * n let s = c * n
proc inner () : cstring = ("[$1]" % $n) & s & " <--" proc inner(): cstring = ("[$1]" % $n) & s & " <--"
regCallback (inner) regCallback(inner)
return (s, n) return (s, n)
var expected = "\n" var expected = "\n"
for i in 1 .. 10: for i in 1 .. 10:
let (s, n) = outer (i) let (s, n) = outer(i)
expected &= ("($1)[$2]" % [$i, $n]) & s & " <--" expected &= ("($1)[$2]" % [$i, $n]) & s & " <--"
expected &= "\n" expected &= "\n"
let results = runCallbacks () let results = runCallbacks()
doAssert(expected == results) doAssert(expected == results)

View file

@ -21,7 +21,7 @@ macro debug(n: varargs[untyped]): untyped =
add(result, newCall("writeLine", newIdentNode("stdout"), n[i])) add(result, newCall("writeLine", newIdentNode("stdout"), n[i]))
var var
a: array [0..10, int] a: array[0..10, int]
x = "some string" x = "some string"
a[0] = 42 a[0] = 42
a[1] = 45 a[1] = 45

View file

@ -58,10 +58,10 @@ proc create(T: typedesc): T =
result.vtbl = getVTable(T) result.vtbl = getVTable(T)
proc baseFoo(o: var TBase): int = 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) = proc baseBar(o: var TBase) =
cast[barProc[TBase]](o.vtbl[1]) (o) cast[barProc[TBase]](o.vtbl[1])(o)
var a = TUserObject1.create var a = TUserObject1.create
var b = TUserObject2.create var b = TUserObject2.create

View file

@ -8,7 +8,7 @@ type
myEnum = enum e1, e2, e3, e4, e5 myEnum = enum e1, e2, e3, e4, e5
var var
a: array [myEnum, int] a: array[myEnum, int]
for i in low(a) .. high(a): for i in low(a) .. high(a):
a[i] = 0 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 # Test a submodule
#type #type
# TStringArr = array [0.. *] of string # TStringArr = array[0.. *] of string
proc exportme* = discard proc exportme* = discard

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