first steps to C file merge operation for incremental compilation

This commit is contained in:
Araq
2011-10-21 01:06:24 +02:00
parent 7ebaf44897
commit a6f90d4cdd
8 changed files with 441 additions and 118 deletions

View File

@@ -720,6 +720,11 @@ proc IdTableGet(t: TIdTable, key: int): PObject =
var index = IdTableRawGet(t, key)
if index >= 0: result = t.data[index].val
else: result = nil
iterator pairs*(t: TIdTable): tuple[key: int, value: PObject] =
for i in 0..high(t.data):
if t.data[i].key != nil:
yield (t.data[i].key.id, t.data[i].val)
proc IdTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: PObject) =
var h: THash

253
compiler/ccgmerge.nim Normal file
View File

@@ -0,0 +1,253 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements the merge operation of 2 different C files. This
## is needed for incremental compilation.
import
ast, astalgo, ropes, options, strutils, lexbase, msgs, cgendata, rodutils,
intsets, platform, llstream
# Careful! Section marks need to contain a tabulator so that they cannot
# be part of C string literals.
const
CFileSectionNames: array[TCFileSection, string] = [
cfsMergeInfo: "",
cfsHeaders: "NIM_merge_HEADERS",
cfsForwardTypes: "NIM_merge_FORWARD_TYPES",
cfsTypes: "NIM_merge_TYPES",
cfsSeqTypes: "NIM_merge_SEQ_TYPES",
cfsFieldInfo: "NIM_merge_FIELD_INFO",
cfsTypeInfo: "NIM_merge_TYPE_INFO",
cfsProcHeaders: "NIM_merge_PROC_HEADERS",
cfsData: "NIM_merge_DATA",
cfsVars: "NIM_merge_VARS",
cfsProcs: "NIM_merge_PROCS",
cfsInitProc: "NIM_merge_INIT_PROC",
cfsTypeInit1: "NIM_merge_TYPE_INIT1",
cfsTypeInit2: "NIM_merge_TYPE_INIT2",
cfsTypeInit3: "NIM_merge_TYPE_INIT3",
cfsDebugInit: "NIM_merge_DEBUG_INIT",
cfsDynLibInit: "NIM_merge_DYNLIB_INIT",
cfsDynLibDeinit: "NIM_merge_DYNLIB_DEINIT",
]
CProcSectionNames: array[TCProcSection, string] = [
cpsLocals: "NIM_merge_PROC_LOCALS",
cpsInit: "NIM_merge_PROC_INIT",
cpsStmts: "NIM_merge_PROC_BODY"
]
proc genSectionStart*(fs: TCFileSection): PRope =
if optSymbolFiles in gGlobalOptions:
result = toRope(tnl)
app(result, "/*\t")
app(result, CFileSectionNames[fs])
app(result, "*/")
app(result, tnl)
proc genSectionEnd*(fs: TCFileSection): PRope =
if optSymbolFiles in gGlobalOptions:
result = toRope("/*\tNIM_merge_END*/" & tnl)
proc genSectionStart*(ps: TCProcSection): PRope =
if optSymbolFiles in gGlobalOptions:
result = toRope(tnl)
app(result, "/*\t")
app(result, CProcSectionNames[ps])
app(result, "*/")
app(result, tnl)
proc genSectionEnd*(ps: TCProcSection): PRope =
if optSymbolFiles in gGlobalOptions:
result = toRope("/*\tNIM_merge_END*/" & tnl)
proc writeTypeCache(a: TIdTable, s: var string) =
var i = 0
for id, value in pairs(a):
if i == 10:
i = 0
s.add(tnl)
else:
s.add(' ')
encodeVInt(id, s)
s.add(':')
encodeStr(PRope(value).ropeToStr, s)
inc i
s.add('}')
proc writeIntSet(a: TIntSet, s: var string) =
var i = 0
for x in items(a):
if i == 10:
i = 0
s.add(tnl)
else:
s.add(' ')
encodeVInt(x, s)
inc i
s.add('}')
proc genMergeInfo*(m: BModule): PRope =
if optSymbolFiles notin gGlobalOptions: return nil
var s = "/*\tNIM_merge_INFO:"
s.add(tnl)
s.add("typeCache:{")
writeTypeCache(m.typeCache, s)
s.add("declared:{")
writeIntSet(m.declaredThings, s)
s.add("typeInfo:{")
writeIntSet(m.typeInfoMarker, s)
s.add("labels:")
encodeVInt(m.labels, s)
s.add(tnl)
s.add("*/")
result = s.toRope
template `^`(pos: expr): expr = L.buf[pos]
proc skipWhite(L: var TBaseLexer) =
var pos = L.bufpos
while true:
case ^pos
of CR: pos = lexbase.HandleCR(L, pos)
of LF: pos = lexbase.HandleLF(L, pos)
of ' ': inc pos
else: break
L.bufpos = pos
proc skipUntilCmd(L: var TBaseLexer) =
var pos = L.bufpos
while true:
case ^pos
of CR: pos = lexbase.HandleCR(L, pos)
of LF: pos = lexbase.HandleLF(L, pos)
of '\0': break
of '/':
if ^(pos+1) == '*' and ^(pos+2) == '\t':
inc pos, 3
break
inc pos
else: inc pos
L.bufpos = pos
proc readVerbatimSection(L: var TBaseLexer): PRope =
const section = "/*\tNIM_merge_END*/"
var pos = L.bufpos
var buf = L.buf
result = newMutableRope(30_000)
while true:
case buf[pos]
of CR:
pos = lexbase.HandleCR(L, pos)
buf = L.buf
result.data.add(tnl)
of LF:
pos = lexbase.HandleLF(L, pos)
buf = L.buf
result.data.add(tnl)
of '\0': break
else: nil
if buf[pos] == section[0]:
var s = 0
while buf[pos+1] == section[s+1]:
inc s
inc pos
if section[s] != '\0':
# reset:
dec pos, s
else:
break
result.data.add(buf[pos])
inc pos
L.bufpos = pos
result.length = result.data.len
proc readKey(L: var TBaseLexer): string =
var pos = L.bufpos
var buf = L.buf
while buf[pos] in IdentChars:
result.add(buf[pos])
inc pos
if buf[pos] != ':': internalError("ccgmerge: ':' expected")
L.bufpos = pos + 1 # skip ':'
proc NewFakeType(id: int): PType =
new(result)
result.id = id
proc readTypeCache(L: var TBaseLexer, result: var TIdTable) =
if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
inc L.bufpos
while ^L.bufpos != '}':
skipWhite(L)
var key = decodeVInt(L.buf, L.bufpos)
if ^L.bufpos != ':': internalError("ccgmerge: ':' expected")
inc L.bufpos
var value = decodeStr(L.buf, L.bufpos)
# XXX little hack: we create a "fake" type object with the correct Id
# better would be to adapt the data structure to not even store the
# object as key, but only the Id
IdTablePut(result, newFakeType(key), value.toRope)
inc L.bufpos
proc readIntSet(L: var TBaseLexer, result: var TIntSet) =
if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
inc L.bufpos
while ^L.bufpos != '}':
skipWhite(L)
var key = decodeVInt(L.buf, L.bufpos)
result.incl(key)
inc L.bufpos
proc processMergeInfo(L: var TBaseLexer, m: BModule) =
while true:
skipWhite(L)
if ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
inc(L.bufpos, 2)
break
var k = readKey(L)
case k
of "typeCache": readTypeCache(L, m.typeCache)
of "declared": readIntSet(L, m.declaredThings)
of "typeInfo": readIntSet(L, m.typeInfoMarker)
of "labels": m.labels = decodeVInt(L.buf, L.bufpos)
else: InternalError("ccgmerge: unkown key: " & k)
proc readMergeInfo*(cfilename: string, m: BModule) =
## reads the merge information into `m`.
var s = LLStreamOpen(cfilename, fmRead)
if s == nil: return
var L: TBaseLexer
openBaseLexer(L, s)
while true:
skipUntilCmd(L)
if ^L.bufpos == '\0': break
var k = readKey(L)
if k == "NIM_merge_INFO":
processMergeInfo(L, m)
elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
inc(L.bufpos, 2)
# read back into section
skipWhite(L)
var verbatim = readVerbatimSection(L)
skipWhite(L)
var sectionA = CFileSectionNames.find(k)
if sectionA > 0 and sectionA <= high(TCFileSection).int:
m.s[TCFileSection(sectionA)] = verbatim
else:
var sectionB = CProcSectionNames.find(k)
if sectionB >= 0 and sectionB <= high(TCProcSection).int:
m.initProc.s[TCProcSection(sectionB)] = verbatim
else:
InternalError("ccgmerge: unknown section: " & k)
else:
InternalError("ccgmerge: */ expected")
closeBaseLexer(L)

View File

@@ -15,7 +15,7 @@ import
options, intsets,
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
rodutils, renderer, idgen
rodutils, renderer, idgen, cgendata, ccgmerge
when options.hasTinyCBackend:
import tccgen
@@ -23,96 +23,6 @@ when options.hasTinyCBackend:
proc cgenPass*(): TPass
# implementation
type
TLabel = PRope # for the C generator a label is just a rope
TCFileSection = enum # the sections a generated C file consists of
cfsHeaders, # section for C include file headers
cfsForwardTypes, # section for C forward typedefs
cfsTypes, # section for C typedefs
cfsSeqTypes, # section for sequence types only
# this is needed for strange type generation
# reasons
cfsFieldInfo, # section for field information
cfsTypeInfo, # section for type information
cfsProcHeaders, # section for C procs prototypes
cfsData, # section for C constant data
cfsVars, # section for C variable declarations
cfsProcs, # section for C procs that are not inline
cfsTypeInit1, # section 1 for declarations of type information
cfsTypeInit2, # section 2 for init of type information
cfsTypeInit3, # section 3 for init of type information
cfsDebugInit, # section for init of debug information
cfsDynLibInit, # section for init of dynamic library binding
cfsDynLibDeinit # section for deinitialization of dynamic
# libraries
TCTypeKind = enum # describes the type kind of a C type
ctVoid, ctChar, ctBool, ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
ctInt, ctInt8, ctInt16, ctInt32, ctInt64, ctFloat, ctFloat32, ctFloat64,
ctFloat128, ctArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc, ctCString
TCFileSections = array[TCFileSection, PRope] # represents a generated C file
TCProcSection = enum # the sections a generated C proc consists of
cpsLocals, # section of local variables for C proc
cpsInit, # section for init of variables for C proc
cpsStmts # section of local statements for C proc
TCProcSections = array[TCProcSection, PRope] # represents a generated C proc
BModule = ref TCGen
BProc = ref TCProc
TBlock{.final.} = object
id*: int # the ID of the label; positive means that it
# has been used (i.e. the label should be emitted)
nestedTryStmts*: int # how many try statements is it nested into
TCProc{.final.} = object # represents C proc that is currently generated
s: TCProcSections # the procs sections; short name for readability
prc: PSym # the Nimrod proc that this C proc belongs to
BeforeRetNeeded: bool # true iff 'BeforeRet' label for proc is needed
ThreadVarAccessed: bool # true if the proc already accessed some threadvar
nestedTryStmts: seq[PNode] # in how many nested try statements we are
# (the vars must be volatile then)
labels: Natural # for generating unique labels in the C proc
blocks: seq[TBlock] # nested blocks
options: TOptions # options that should be used for code
# generation; this is the same as prc.options
# unless prc == nil
frameLen: int # current length of frame descriptor
sendClosure: PType # closure record type that we pass
receiveClosure: PType # closure record type that we get
module: BModule # used to prevent excessive parameter passing
withinLoop: int # > 0 if we are within a loop
TTypeSeq = seq[PType]
TCGen = object of TPassContext # represents a C source file
module*: PSym
filename*: string
s*: TCFileSections # sections of the C file
PreventStackTrace: bool # true if stack traces need to be prevented
usesThreadVars: bool # true if the module uses a thread var
cfilename*: string # filename of the module (including path,
# without extension)
typeCache*: TIdTable # cache the generated types
forwTypeCache*: TIdTable # cache for forward declarations of types
declaredThings*: TIntSet # things we have declared in this .c file
declaredProtos*: TIntSet # prototypes we have declared in this .c file
headerFiles*: TLinkedList # needed headers to include
typeInfoMarker*: TIntSet # needed for generating type information
initProc*: BProc # code for init procedure
typeStack*: TTypeSeq # used for type generation
dataCache*: TNodeTable
forwardedProcs*: TSymSeq # keep forwarded procs here
typeNodes*, nimTypes*: int # used for type info generation
typeNodesName*, nimTypesName*: PRope # used for type info generation
labels*: natural # for generating unique module-scope names
var
mainModProcs, mainModInit: PRope # parts of the main module
gMapping: PRope # the generated mapping file (if requested)
gProcProfile: Natural # proc profile counter
gGeneratedSyms: TIntSet # set of ID's of generated symbols
gPendingModules: seq[BModule] = @[] # list of modules that are not
# finished with code generation
gForwardedProcsCounter: int = 0
gNimDat: BModule # generated global data
proc ropeff(cformat, llvmformat: string, args: openarray[PRope]): PRope =
if gCmd == cmdCompileToLLVM: result = ropef(llvmformat, args)
else: result = ropef(cformat, args)
@@ -157,15 +67,6 @@ proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: PRope, s: TStorageLoc) =
a.s = s
if a.r == nil: a.r = r
proc newProc(prc: PSym, module: BModule): BProc =
new(result)
result.prc = prc
result.module = module
if prc != nil: result.options = prc.options
else: result.options = gOptions
result.blocks = @[]
result.nestedTryStmts = @[]
proc isSimpleConst(typ: PType): bool =
result = not (skipTypes(typ, abstractVar).kind in
{tyTuple, tyObject, tyArray, tyArrayConstr, tySet, tySequence})
@@ -864,31 +765,53 @@ proc genInitCode(m: BModule) =
# BUT: the generated init code might depend on a current frame, so
# declare it nevertheless:
getFrameDecl(m.initProc)
app(prc, genSectionStart(cpsLocals))
app(prc, m.initProc.s[cpsLocals])
app(prc, genSectionEnd(cpsLocals))
app(prc, genSectionStart(cfsTypeInit1))
app(prc, m.s[cfsTypeInit1])
if optStackTrace in m.initProc.options and not m.PreventStackTrace:
app(prc, m.initProc.s[cpsLocals])
app(prc, m.s[cfsTypeInit1])
var procname = CStringLit(m.initProc, prc, m.module.name.s)
var filename = CStringLit(m.initProc, prc, toFilename(m.module.info))
app(prc, initFrame(m.initProc, procname, filename))
else:
app(prc, m.initProc.s[cpsLocals])
app(prc, m.s[cfsTypeInit1])
app(prc, m.s[cfsTypeInit2])
app(prc, m.s[cfsTypeInit3])
app(prc, m.s[cfsDebugInit])
app(prc, m.s[cfsDynLibInit])
app(prc, genSectionEnd(cfsTypeInit1))
for i in cfsTypeInit2..cfsDynLibInit:
app(prc, genSectionStart(i))
app(prc, m.s[i])
app(prc, genSectionEnd(i))
app(prc, genSectionStart(cpsInit))
app(prc, m.initProc.s[cpsInit])
app(prc, genSectionEnd(cpsInit))
app(prc, genSectionStart(cpsStmts))
app(prc, m.initProc.s[cpsStmts])
if optStackTrace in m.initProc.options and not m.PreventStackTrace:
app(prc, deinitFrame(m.initProc))
app(prc, genSectionEnd(cpsStmts))
appf(prc, "}$n$n")
app(m.s[cfsProcs], prc)
# we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because
# that would lead to a *nesting* of merge sections which the merger does
# not support. So we add it to another special section: ``cfsInitProc``
app(m.s[cfsInitProc], prc)
proc genModule(m: BModule, cfilenoext: string): PRope =
result = getFileHeader(cfilenoext)
result.app(genMergeInfo(m))
app(m.s[cfsHeaders], genSectionStart(cfsHeaders))
generateHeaders(m)
app(m.s[cfsHeaders], genSectionEnd(cfsHeaders))
generateThreadLocalStorage(m)
for i in countup(low(TCFileSection), cfsProcs): app(result, m.s[i])
for i in countup(low(TCFileSection), cfsProcs):
app(result, genSectionStart(i))
app(result, m.s[i])
app(result, genSectionEnd(i))
app(result, m.s[cfsInitProc])
proc rawNewModule(module: PSym, filename: string): BModule =
new(result)

116
compiler/cgendata.nim Normal file
View File

@@ -0,0 +1,116 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module contains the data structures for the C code generation phase.
import
ast, astalgo, ropes, passes, options, intsets, lists, platform
type
TLabel* = PRope # for the C generator a label is just a rope
TCFileSection* = enum # the sections a generated C file consists of
cfsMergeInfo, # section containing merge information
cfsHeaders, # section for C include file headers
cfsForwardTypes, # section for C forward typedefs
cfsTypes, # section for C typedefs
cfsSeqTypes, # section for sequence types only
# this is needed for strange type generation
# reasons
cfsFieldInfo, # section for field information
cfsTypeInfo, # section for type information
cfsProcHeaders, # section for C procs prototypes
cfsData, # section for C constant data
cfsVars, # section for C variable declarations
cfsProcs, # section for C procs that are not inline
cfsInitProc, # section for the C init proc
cfsTypeInit1, # section 1 for declarations of type information
cfsTypeInit2, # section 2 for init of type information
cfsTypeInit3, # section 3 for init of type information
cfsDebugInit, # section for init of debug information
cfsDynLibInit, # section for init of dynamic library binding
cfsDynLibDeinit # section for deinitialization of dynamic
# libraries
TCTypeKind* = enum # describes the type kind of a C type
ctVoid, ctChar, ctBool, ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
ctInt, ctInt8, ctInt16, ctInt32, ctInt64, ctFloat, ctFloat32, ctFloat64,
ctFloat128, ctArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc, ctCString
TCFileSections = array[TCFileSection, PRope] # represents a generated C file
TCProcSection* = enum # the sections a generated C proc consists of
cpsLocals, # section of local variables for C proc
cpsInit, # section for init of variables for C proc
cpsStmts # section of local statements for C proc
TCProcSections = array[TCProcSection, PRope] # represents a generated C proc
BModule* = ref TCGen
BProc* = ref TCProc
TBlock{.final.} = object
id*: int # the ID of the label; positive means that it
# has been used (i.e. the label should be emitted)
nestedTryStmts*: int # how many try statements is it nested into
TCProc{.final.} = object # represents C proc that is currently generated
s*: TCProcSections # the procs sections; short name for readability
prc*: PSym # the Nimrod proc that this C proc belongs to
BeforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
ThreadVarAccessed*: bool # true if the proc already accessed some threadvar
nestedTryStmts*: seq[PNode] # in how many nested try statements we are
# (the vars must be volatile then)
labels*: Natural # for generating unique labels in the C proc
blocks*: seq[TBlock] # nested blocks
options*: TOptions # options that should be used for code
# generation; this is the same as prc.options
# unless prc == nil
frameLen*: int # current length of frame descriptor
sendClosure*: PType # closure record type that we pass
receiveClosure*: PType # closure record type that we get
module*: BModule # used to prevent excessive parameter passing
withinLoop*: int # > 0 if we are within a loop
TTypeSeq* = seq[PType]
TCGen = object of TPassContext # represents a C source file
module*: PSym
filename*: string
s*: TCFileSections # sections of the C file
PreventStackTrace*: bool # true if stack traces need to be prevented
usesThreadVars*: bool # true if the module uses a thread var
cfilename*: string # filename of the module (including path,
# without extension)
typeCache*: TIdTable # cache the generated types
forwTypeCache*: TIdTable # cache for forward declarations of types
declaredThings*: TIntSet # things we have declared in this .c file
declaredProtos*: TIntSet # prototypes we have declared in this .c file
headerFiles*: TLinkedList # needed headers to include
typeInfoMarker*: TIntSet # needed for generating type information
initProc*: BProc # code for init procedure
typeStack*: TTypeSeq # used for type generation
dataCache*: TNodeTable
forwardedProcs*: TSymSeq # keep forwarded procs here
typeNodes*, nimTypes*: int # used for type info generation
typeNodesName*, nimTypesName*: PRope # used for type info generation
labels*: natural # for generating unique module-scope names
var
mainModProcs*, mainModInit*: PRope # parts of the main module
gMapping*: PRope # the generated mapping file (if requested)
gProcProfile*: Natural # proc profile counter
gGeneratedSyms*: TIntSet # set of ID's of generated symbols
gPendingModules*: seq[BModule] = @[] # list of modules that are not
# finished with code generation
gForwardedProcsCounter*: int = 0
gNimDat*: BModule # generated global data
proc newProc*(prc: PSym, module: BModule): BProc =
new(result)
result.prc = prc
result.module = module
if prc != nil: result.options = prc.options
else: result.options = gOptions
result.blocks = @[]
result.nestedTryStmts = @[]

View File

@@ -9,7 +9,7 @@
## This module contains a simple persistent id generator.
import idents, strutils, os
import idents, strutils, os, options
var gFrontEndId, gBackendId*: int
@@ -40,15 +40,18 @@ proc setId*(id: int) {.inline.} =
proc IDsynchronizationPoint*(idRange: int) =
gFrontEndId = (gFrontEndId div IdRange + 1) * IdRange + 1
proc toGid(f: string): string =
result = options.completeGeneratedFilePath(f.addFileExt("gid"))
proc saveMaxIds*(project: string) =
var f = open(project.addFileExt("gid"), fmWrite)
var f = open(project.toGid, fmWrite)
f.writeln($gFrontEndId)
f.writeln($gBackEndId)
f.close()
proc loadMaxIds*(project: string) =
var f: TFile
if open(f, project.addFileExt("gid"), fmRead):
if open(f, project.toGid, fmRead):
var frontEndId = parseInt(f.readLine)
var backEndId = parseInt(f.readLine)
gFrontEndId = max(gFrontEndId, frontEndId)

View File

@@ -59,8 +59,8 @@ const
chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
# since negative numbers require a leading '-' they use up 1 byte. Thus we
# subtract/add vintDelta here to save space for little negative numbers
# which are common in ROD files:
# subtract/add `vintDelta` here to save space for small negative numbers
# which are common in ROD files:
const
vintDelta = 5

View File

@@ -111,6 +111,12 @@ proc newRope(data: string = nil): PRope =
result.length = len(data)
result.data = data
proc newMutableRope*(capacity = 30): PRope =
## creates a new rope that supports direct modifications of the rope's
## 'data' and 'length' fields.
new(result)
result.data = newStringOfCap(capacity)
var
cache: PRope # the root of the cache tree
misses, hits: int

View File

@@ -2,19 +2,33 @@ Version 0.8.14
==============
- optimize unused constants away
- 'let x = y'; const ptr/ref
- 'let x = y'
- fix actors.nim
- make threadvar efficient again on linux after testing
- test the sort implementation again
- optional indentation for 'case' statement
- document & test splicing; don't forget to test negative indexes
- thread local vs. global raiseHook()
- incremental compilation (!)
- make pegs support a compile-time option and make c2nim use regexes instead
per default
incremental compilation
-----------------------
- implement C file merge operation
- adapt thread var implementation to care about the new merge operation
- write test cases: needs test script support
- fix remaining bugs
- write documentation
- make the compiler output a warning if linking fails with --symbolFiles:on
(necessary?)
version 0.9.0
=============
- const ptr/ref
- unsigned ints and bignums
- warning for implicit openArray -> varargs convention
- implement explicit varargs
@@ -24,6 +38,8 @@ version 0.9.0
- make exceptions compatible with C++ exceptions
- ``=`` should be overloadable; requires specialization for ``=``
- 'const' objects including case objects
- os module should use Windows Unicode versions
- 64bit build for Windows
Bugs
----
@@ -141,5 +157,6 @@ Version 2
a full blown statement; a ``try`` expression might be a good idea to make
error handling more light-weight
people also want ``inc a; inc b``
--> solved by providing an expr version of most control structures?