Merge branch 'devel' of github.com:Araq/Nimrod into devel

This commit is contained in:
Zahary Karadjov 2014-03-06 23:25:45 +02:00
commit 2cbe46daff
23 changed files with 280 additions and 55 deletions

View file

@ -489,6 +489,8 @@ const
routineKinds* = {skProc, skMethod, skIterator, skClosureIterator, routineKinds* = {skProc, skMethod, skIterator, skClosureIterator,
skConverter, skMacro, skTemplate} skConverter, skMacro, skTemplate}
tfIncompleteStruct* = tfVarargs tfIncompleteStruct* = tfVarargs
tfUncheckedArray* = tfVarargs
tfUnion* = tfNoSideEffect
skError* = skUnknown skError* = skUnknown
# type flags that are essential for type equality: # type flags that are essential for type equality:

View file

@ -681,11 +681,6 @@ proc parseField(p: var TParser, kind: TNodeKind): PNode =
else: result = mangledIdent(p.tok.s, p) else: result = mangledIdent(p.tok.s, p)
getTok(p, result) getTok(p, result)
proc takeOnlyFirstField(p: TParser, isUnion: bool): bool =
# if we generate an interface to a header file, *all* fields can be
# generated:
result = isUnion and p.options.header.len == 0
proc parseStructBody(p: var TParser, isUnion: bool, proc parseStructBody(p: var TParser, isUnion: bool,
kind: TNodeKind = nkRecList): PNode = kind: TNodeKind = nkRecList): PNode =
result = newNodeP(kind, p) result = newNodeP(kind, p)
@ -698,8 +693,7 @@ proc parseStructBody(p: var TParser, isUnion: bool,
var i = parseField(p, kind) var i = parseField(p, kind)
t = parseTypeSuffix(p, t) t = parseTypeSuffix(p, t)
addSon(def, i, t, ast.emptyNode) addSon(def, i, t, ast.emptyNode)
if not takeOnlyFirstField(p, isUnion) or sonsLen(result) < 1: addSon(result, def)
addSon(result, def)
if p.tok.xkind != pxComma: break if p.tok.xkind != pxComma: break
getTok(p, def) getTok(p, def)
eat(p, pxSemicolon, lastSon(result)) eat(p, pxSemicolon, lastSon(result))
@ -710,11 +704,12 @@ proc structPragmas(p: TParser, name: PNode, origName: string): PNode =
result = newNodeP(nkPragmaExpr, p) result = newNodeP(nkPragmaExpr, p)
addSon(result, exportSym(p, name, origName)) addSon(result, exportSym(p, name, origName))
var pragmas = newNodeP(nkPragma, p) var pragmas = newNodeP(nkPragma, p)
addSon(pragmas, newIdentNodeP("pure", p), newIdentNodeP("final", p)) #addSon(pragmas, newIdentNodeP("pure", p), newIdentNodeP("final", p))
if p.options.header.len > 0: if p.options.header.len > 0:
addSon(pragmas, newIdentStrLitPair("importc", origName, p), addSon(pragmas, newIdentStrLitPair("importc", origName, p),
newIdentStrLitPair("header", p.options.header, p)) newIdentStrLitPair("header", p.options.header, p))
addSon(result, pragmas) if pragmas.len > 0: addSon(result, pragmas)
else: addSon(result, ast.emptyNode)
proc enumPragmas(p: TParser, name: PNode): PNode = proc enumPragmas(p: TParser, name: PNode): PNode =
result = newNodeP(nkPragmaExpr, p) result = newNodeP(nkPragmaExpr, p)
@ -728,7 +723,11 @@ proc enumPragmas(p: TParser, name: PNode): PNode =
proc parseStruct(p: var TParser, isUnion: bool): PNode = proc parseStruct(p: var TParser, isUnion: bool): PNode =
result = newNodeP(nkObjectTy, p) result = newNodeP(nkObjectTy, p)
addSon(result, ast.emptyNode, ast.emptyNode) # no pragmas, no inheritance var pragmas = ast.emptyNode
if isUnion:
pragmas = newNodeP(nkPragma, p)
addSon(pragmas, newIdentNodeP("union", p))
addSon(result, pragmas, ast.emptyNode) # no inheritance
if p.tok.xkind == pxCurlyLe: if p.tok.xkind == pxCurlyLe:
addSon(result, parseStructBody(p, isUnion)) addSon(result, parseStructBody(p, isUnion))
else: else:

View file

@ -161,6 +161,13 @@ proc hashType(c: var MD5Context, t: PType) =
if tfShared in t.flags: c &= "shared" if tfShared in t.flags: c &= "shared"
if tfNotNil in t.flags: c &= "not nil" if tfNotNil in t.flags: c &= "not nil"
proc canonConst(n: PNode): TUid =
var c: MD5Context
md5Init(c)
c.hashTree(n)
c.hashType(n.typ)
md5Final(c, MD5Digest(result))
proc canonSym(s: PSym): TUid proc canonSym(s: PSym): TUid
var c: MD5Context var c: MD5Context
md5Init(c) md5Init(c)

View file

@ -736,7 +736,7 @@ proc genArrayElem(p: BProc, e: PNode, d: var TLoc) =
var ty = skipTypes(skipTypes(a.t, abstractVarRange), abstractPtrs) var ty = skipTypes(skipTypes(a.t, abstractVarRange), abstractPtrs)
var first = intLiteral(firstOrd(ty)) var first = intLiteral(firstOrd(ty))
# emit range check: # emit range check:
if (optBoundsCheck in p.options): if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags:
if not isConstExpr(e.sons[1]): if not isConstExpr(e.sons[1]):
# semantic pass has already checked for const index expressions # semantic pass has already checked for const index expressions
if firstOrd(ty) == 0: if firstOrd(ty) == 0:

View file

@ -377,9 +377,12 @@ proc getTypePre(m: BModule, typ: PType): PRope =
result = getSimpleTypeDesc(m, typ) result = getSimpleTypeDesc(m, typ)
if result == nil: result = cacheGetType(m.typeCache, typ) if result == nil: result = cacheGetType(m.typeCache, typ)
proc structOrUnion(t: PType): PRope =
(if tfUnion in t.flags: toRope("union") else: toRope("struct"))
proc getForwardStructFormat(): string = proc getForwardStructFormat(): string =
if gCmd == cmdCompileToCpp: result = "struct $1;$n" if gCmd == cmdCompileToCpp: result = "$1 $2;$n"
else: result = "typedef struct $1 $1;$n" else: result = "typedef $1 $2 $2;$n"
proc getTypeForward(m: BModule, typ: PType): PRope = proc getTypeForward(m: BModule, typ: PType): PRope =
result = cacheGetType(m.forwTypeCache, typ) result = cacheGetType(m.forwTypeCache, typ)
@ -390,7 +393,8 @@ proc getTypeForward(m: BModule, typ: PType): PRope =
of tySequence, tyTuple, tyObject: of tySequence, tyTuple, tyObject:
result = getTypeName(typ) result = getTypeName(typ)
if not isImportedType(typ): if not isImportedType(typ):
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result]) appf(m.s[cfsForwardTypes], getForwardStructFormat(),
[structOrUnion(typ), result])
idTablePut(m.forwTypeCache, typ, result) idTablePut(m.forwTypeCache, typ, result)
else: internalError("getTypeForward(" & $typ.kind & ')') else: internalError("getTypeForward(" & $typ.kind & ')')
@ -445,7 +449,12 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname]) if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname])
else: ae = sname else: ae = sname
fillLoc(field.loc, locField, field.typ, ae, OnUnknown) fillLoc(field.loc, locField, field.typ, ae, OnUnknown)
appf(result, "$1 $2;$n", [getTypeDescAux(m, field.loc.t, check), sname]) let fieldType = field.loc.t
if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags:
appf(result, "$1 $2[SEQ_DECL_SIZE];$n",
[getTypeDescAux(m, fieldType.elemType, check), sname])
else:
appf(result, "$1 $2;$n", [getTypeDescAux(m, fieldType, check), sname])
else: internalError(n.info, "genRecordFieldsAux()") else: internalError(n.info, "genRecordFieldsAux()")
proc getRecordFields(m: BModule, typ: PType, check: var TIntSet): PRope = proc getRecordFields(m: BModule, typ: PType, check: var TIntSet): PRope =
@ -455,23 +464,24 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope,
check: var TIntSet): PRope = check: var TIntSet): PRope =
# declare the record: # declare the record:
var hasField = false var hasField = false
let aStruct = structOrUnion(typ)
if typ.kind == tyObject: if typ.kind == tyObject:
if typ.sons[0] == nil: if typ.sons[0] == nil:
if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags: if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags:
result = ropecg(m, "struct $1 {$n", [name]) result = ropecg(m, "$1 $2 {$n", [aStruct, name])
else: else:
result = ropecg(m, "struct $1 {$n#TNimType* m_type;$n", [name]) result = ropecg(m, "$1 $2 {$n#TNimType* m_type;$n", [aStruct, name])
hasField = true hasField = true
elif gCmd == cmdCompileToCpp: elif gCmd == cmdCompileToCpp:
result = ropecg(m, "struct $1 : public $2 {$n", result = ropecg(m, "$1 $2 : public $3 {$n",
[name, getTypeDescAux(m, typ.sons[0], check)]) [aStruct, name, getTypeDescAux(m, typ.sons[0], check)])
hasField = true hasField = true
else: else:
result = ropecg(m, "struct $1 {$n $2 Sup;$n", result = ropecg(m, "$1 $2 {$n $3 Sup;$n",
[name, getTypeDescAux(m, typ.sons[0], check)]) [aStruct, name, getTypeDescAux(m, typ.sons[0], check)])
hasField = true hasField = true
else: else:
result = ropef("struct $1 {$n", [name]) result = ropef("$1 $2 {$n", [aStruct, name])
var desc = getRecordFields(m, typ, check) var desc = getRecordFields(m, typ, check)
if (desc == nil) and not hasField: if (desc == nil) and not hasField:
appf(result, "char dummy;$n", []) appf(result, "char dummy;$n", [])
@ -481,7 +491,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope,
proc getTupleDesc(m: BModule, typ: PType, name: PRope, proc getTupleDesc(m: BModule, typ: PType, name: PRope,
check: var TIntSet): PRope = check: var TIntSet): PRope =
result = ropef("struct $1 {$n", [name]) result = ropef("$1 $2 {$n", [structOrUnion(typ), name])
var desc: PRope = nil var desc: PRope = nil
for i in countup(0, sonsLen(typ) - 1): for i in countup(0, sonsLen(typ) - 1):
appf(desc, "$1 Field$2;$n", appf(desc, "$1 Field$2;$n",
@ -557,7 +567,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope =
if result == nil: if result == nil:
result = getTypeName(t) result = getTypeName(t)
if not isImportedType(t): if not isImportedType(t):
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result]) appf(m.s[cfsForwardTypes], getForwardStructFormat(),
[structOrUnion(t), result])
idTablePut(m.forwTypeCache, t, result) idTablePut(m.forwTypeCache, t, result)
assert(cacheGetType(m.typeCache, t) == nil) assert(cacheGetType(m.typeCache, t) == nil)
idTablePut(m.typeCache, t, con(result, "*")) idTablePut(m.typeCache, t, con(result, "*"))
@ -588,7 +599,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope =
if result == nil: if result == nil:
result = getTypeName(t) result = getTypeName(t)
if not isImportedType(t): if not isImportedType(t):
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result]) appf(m.s[cfsForwardTypes], getForwardStructFormat(),
[structOrUnion(t), result])
idTablePut(m.forwTypeCache, t, result) idTablePut(m.forwTypeCache, t, result)
idTablePut(m.typeCache, t, result) # always call for sideeffects: idTablePut(m.typeCache, t, result) # always call for sideeffects:
if t.kind != tyTuple: recdesc = getRecordDesc(m, t, result, check) if t.kind != tyTuple: recdesc = getRecordDesc(m, t, result, check)

View file

@ -47,6 +47,7 @@ proc initDefines*() =
defineSymbol("nimeffects") defineSymbol("nimeffects")
defineSymbol("nimbabel") defineSymbol("nimbabel")
defineSymbol("nimcomputedgoto") defineSymbol("nimcomputedgoto")
defineSymbol("nimunion")
# add platform specific symbols: # add platform specific symbols:
case targetCPU case targetCPU

View file

@ -92,7 +92,7 @@ proc errorSym*(c: PContext, n: PNode): PSym =
result.typ = errorType(c) result.typ = errorType(c)
incl(result.flags, sfDiscardable) incl(result.flags, sfDiscardable)
# pretend it's imported from some unknown module to prevent cascading errors: # pretend it's imported from some unknown module to prevent cascading errors:
if gCmd != cmdInteractive: if gCmd != cmdInteractive and c.inCompilesContext == 0:
c.importTable.addSym(result) c.importTable.addSym(result)
type type

View file

@ -52,7 +52,7 @@ const
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef, wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
wInheritable, wGensym, wInject, wRequiresInit} wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion}
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
wImportCpp, wImportObjC, wError} wImportCpp, wImportObjC, wError}
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
@ -718,6 +718,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
noVal(it) noVal(it)
if sym.typ == nil: invalidPragma(it) if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfIncompleteStruct) else: incl(sym.typ.flags, tfIncompleteStruct)
of wUnchecked:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfUncheckedArray)
of wUnion:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfUnion)
of wRequiresInit: of wRequiresInit:
noVal(it) noVal(it)
if sym.typ == nil: invalidPragma(it) if sym.typ == nil: invalidPragma(it)

View file

@ -1323,7 +1323,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]): if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
voidContext = true voidContext = true
n.typ = enforceVoidContext n.typ = enforceVoidContext
if i == last and efWantValue in flags: if i == last and (length == 1 or efWantValue in flags):
n.typ = n.sons[i].typ n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr if not isEmptyType(n.typ): n.kind = nkStmtListExpr
elif i != last or voidContext or c.inTypeClass > 0: elif i != last or voidContext or c.inTypeClass > 0:

View file

@ -161,6 +161,7 @@ type
PProc* = ref object PProc* = ref object
blocks*: seq[TBlock] # blocks; temp data structure blocks*: seq[TBlock] # blocks; temp data structure
sym*: PSym
slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]] slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]]
maxSlots*: int maxSlots*: int

View file

@ -980,6 +980,25 @@ proc setSlot(c: PCtx; v: PSym) =
kind: if v.kind == skLet: slotFixedLet else: slotFixedVar) kind: if v.kind == skLet: slotFixedLet else: slotFixedVar)
inc c.prc.maxSlots inc c.prc.maxSlots
proc cannotEval(n: PNode) {.noinline.} =
globalError(n.info, errGenerated, "cannot evaluate at compile time: " &
n.renderTree)
proc isOwnedBy(a, b: PSym): bool =
var a = a.owner
while a != nil and a.kind != skModule:
if a == b: return true
a = a.owner
proc checkCanEval(c: PCtx; n: PNode) =
# we need to ensure that we don't evaluate 'x' here:
# proc foo() = var x ...
let s = n.sym
if s.position == 0:
if s.kind in {skVar, skTemp, skLet, skParam, skResult} and
not s.isOwnedBy(c.prc.sym) and s.owner != c.module:
cannotEval(n)
proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
case le.kind case le.kind
of nkBracketExpr: of nkBracketExpr:
@ -1007,6 +1026,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
c.freeTemp(tmp) c.freeTemp(tmp)
of nkSym: of nkSym:
let s = le.sym let s = le.sym
checkCanEval(c, le)
if s.isGlobal: if s.isGlobal:
withTemp(tmp, le.typ): withTemp(tmp, le.typ):
c.gen(le, tmp, {gfAddrOf}) c.gen(le, tmp, {gfAddrOf})
@ -1014,7 +1034,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
c.gABC(le, opcWrDeref, tmp, val) c.gABC(le, opcWrDeref, tmp, val)
c.freeTemp(val) c.freeTemp(val)
else: else:
if s.kind == skForVar and c.mode == emRepl: c.setSlot s if s.kind == skForVar: c.setSlot s
internalAssert s.position > 0 or (s.position == 0 and internalAssert s.position > 0 or (s.position == 0 and
s.kind in {skParam,skResult}) s.kind in {skParam,skResult})
var dest: TRegister = s.position + ord(s.kind == skParam) var dest: TRegister = s.position + ord(s.kind == skParam)
@ -1046,10 +1066,6 @@ proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
localError(info, errGenerated, localError(info, errGenerated,
"cannot 'importc' variable at compile time") "cannot 'importc' variable at compile time")
proc cannotEval(n: PNode) {.noinline.} =
globalError(n.info, errGenerated, "cannot evaluate at compile time: " &
n.renderTree)
proc getNullValue*(typ: PType, info: TLineInfo): PNode proc getNullValue*(typ: PType, info: TLineInfo): PNode
proc genGlobalInit(c: PCtx; n: PNode; s: PSym) = proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
@ -1190,12 +1206,14 @@ proc genVarSection(c: PCtx; n: PNode) =
setSlot(c, a[i].sym) setSlot(c, a[i].sym)
# v = t[i] # v = t[i]
var v: TDest = -1 var v: TDest = -1
checkCanEval(c, a[i])
genRdVar(c, a[i], v, {gfAddrOf}) genRdVar(c, a[i], v, {gfAddrOf})
c.gABC(n, opcWrObj, v, tmp, i) c.gABC(n, opcWrObj, v, tmp, i)
# XXX globals? # XXX globals?
c.freeTemp(tmp) c.freeTemp(tmp)
elif a.sons[0].kind == nkSym: elif a.sons[0].kind == nkSym:
let s = a.sons[0].sym let s = a.sons[0].sym
checkCanEval(c, a.sons[0])
if s.isGlobal: if s.isGlobal:
if s.position == 0: if s.position == 0:
if sfImportc in s.flags: c.importcSym(a.info, s) if sfImportc in s.flags: c.importcSym(a.info, s)
@ -1308,6 +1326,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
case n.kind case n.kind
of nkSym: of nkSym:
let s = n.sym let s = n.sym
checkCanEval(c, n)
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)
@ -1525,7 +1544,7 @@ proc genProc(c: PCtx; s: PSym): int =
# procs easily: # procs easily:
let body = s.getBody let body = s.getBody
let procStart = c.xjmp(body, opcJmp, 0) let procStart = c.xjmp(body, opcJmp, 0)
var p = PProc(blocks: @[]) var p = PProc(blocks: @[], sym: s)
let oldPrc = c.prc let oldPrc = c.prc
c.prc = p c.prc = p
# iterate over the parameters and allocate space for them: # iterate over the parameters and allocate space for them:

View file

@ -63,7 +63,7 @@ type
wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt, wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt,
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
wAsmNoStackFrame, wAsmNoStackFrame,
wImplicitStatic, wGlobal, wCodegenDecl, wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked,
wAuto, wBool, wCatch, wChar, wClass, wAuto, wBool, wCatch, wChar, wClass,
wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast, wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
@ -145,7 +145,7 @@ const
"subschar", "acyclic", "shallow", "unroll", "linearscanend", "subschar", "acyclic", "shallow", "unroll", "linearscanend",
"computedgoto", "injectstmt", "computedgoto", "injectstmt",
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit", "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
"asmnostackframe", "implicitstatic", "global", "codegendecl", "asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked",
"auto", "bool", "catch", "char", "class", "auto", "bool", "catch", "char", "class",
"const_cast", "default", "delete", "double", "const_cast", "default", "delete", "double",

View file

@ -5320,6 +5320,53 @@ strings automatically:
printf("hallo %s", "world") # "world" will be passed as C string printf("hallo %s", "world") # "world" will be passed as C string
Union pragma
------------
The `union`:idx: pragma can be applied to any ``object`` type. It means all
of the object's fields are overlaid in memory. This produces a ``union``
instead of a ``struct`` in the generated C/C++ code. The object declaration
then must not use inheritance or any GC'ed memory but this is currently not
checked.
**Future directions**: GC'ed memory should be allowed in unions and the GC
should scan unions conservatively.
Unchecked pragma
----------------
The `unchecked`:idx: pragma can be used to mark a named array as ``unchecked``
meaning its bounds are not checked. This is often useful when one wishes to
implement his own flexibly sized arrays. Additionally an unchecked array is
translated into a C array of undetermined size:
.. code-block:: nimrod
type
ArrayPart{.unchecked.} = array[0..0, int]
MySeq = object
len, cap: int
data: ArrayPart
Produces roughly this C code:
.. code-block:: C
typedef struct {
NI len;
NI cap;
NI data[];
} MySeq;
The bounds checking done at compile time is not disabled for now, so to access
``s.data[C]`` (where ``C`` is a constant) the array's index needs needs to
include ``C``.
The base type of the unchecked array may not contain any GC'ed memory but this
is currently not checked.
**Future directions**: GC'ed memory should be allowed in unchecked arrays and
there should be an explicit annotation of how the GC is to determine the
runtime size of the array.
Dynlib pragma for import Dynlib pragma for import
------------------------ ------------------------
With the `dynlib`:idx: pragma a procedure or a variable can be imported from With the `dynlib`:idx: pragma a procedure or a variable can be imported from

View file

@ -285,8 +285,8 @@ static N_INLINE(NI32, float32ToInt32)(float x) {
typedef struct TStringDesc* string; typedef struct TStringDesc* string;
/* declared size of a sequence: */ /* declared size of a sequence/variable length array: */
#if defined(__GNUC__) #if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER)
# define SEQ_DECL_SIZE /* empty is correct! */ # define SEQ_DECL_SIZE /* empty is correct! */
#else #else
# define SEQ_DECL_SIZE 1000000 # define SEQ_DECL_SIZE 1000000
@ -373,5 +373,8 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); }
# define GC_GUARD # define GC_GUARD
#endif #endif
/* Test to see if nimrod and the C compiler agrees on the size of a pointer.
On disagreement, your C compiler will say something like:
"error: 'assert_numbits' declared as an array with a negative size" */
typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1]; typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];
#endif #endif

View file

@ -185,6 +185,8 @@ proc `..`*[T](b: T): TSlice[T] {.noSideEffect, inline.} =
when not defined(niminheritable): when not defined(niminheritable):
{.pragma: inheritable.} {.pragma: inheritable.}
when not defined(nimunion):
{.pragma: unchecked.}
const NoFakeVars* = defined(NimrodVM) ## true if the backend doesn't support \ const NoFakeVars* = defined(NimrodVM) ## true if the backend doesn't support \
## "fake variables" like 'var EBADF {.importc.}: cint'. ## "fake variables" like 'var EBADF {.importc.}: cint'.
@ -194,9 +196,10 @@ when not defined(JS):
TGenericSeq {.compilerproc, pure, inheritable.} = object TGenericSeq {.compilerproc, pure, inheritable.} = object
len, reserved: int len, reserved: int
PGenericSeq {.exportc.} = ptr TGenericSeq PGenericSeq {.exportc.} = ptr TGenericSeq
UncheckedCharArray {.unchecked.} = array[0..100_000_000, char]
# len and space without counting the terminating zero: # len and space without counting the terminating zero:
NimStringDesc {.compilerproc, final.} = object of TGenericSeq NimStringDesc {.compilerproc, final.} = object of TGenericSeq
data: array[0..100_000_000, char] data: UncheckedCharArray
NimString = ptr NimStringDesc NimString = ptr NimStringDesc
when not defined(JS) and not defined(NimrodVM): when not defined(JS) and not defined(NimrodVM):
@ -1161,6 +1164,14 @@ when not defined(nimrodVM):
## from it before writing to it is undefined behaviour! ## from it before writing to it is undefined behaviour!
## The allocated memory belongs to its allocating thread! ## The allocated memory belongs to its allocating thread!
## Use `allocShared` to allocate from a shared heap. ## Use `allocShared` to allocate from a shared heap.
proc alloc*(T: typedesc, size = 1): ptr T {.inline.} =
## allocates a new memory block with at least ``T.sizeof * size``
## bytes. The block has to be freed with ``realloc(block, 0)`` or
## ``dealloc(block)``. The block is not initialized, so reading
## from it before writing to it is undefined behaviour!
## The allocated memory belongs to its allocating thread!
## Use `allocShared` to allocate from a shared heap.
cast[ptr T](alloc(T.sizeof * size))
proc alloc0*(size: int): pointer {.noconv, rtl, tags: [].} proc alloc0*(size: int): pointer {.noconv, rtl, tags: [].}
## allocates a new memory block with at least ``size`` bytes. The ## allocates a new memory block with at least ``size`` bytes. The
## block has to be freed with ``realloc(block, 0)`` or ## block has to be freed with ``realloc(block, 0)`` or
@ -1168,14 +1179,31 @@ when not defined(nimrodVM):
## containing zero, so it is somewhat safer than ``alloc``. ## containing zero, so it is somewhat safer than ``alloc``.
## The allocated memory belongs to its allocating thread! ## The allocated memory belongs to its allocating thread!
## Use `allocShared0` to allocate from a shared heap. ## Use `allocShared0` to allocate from a shared heap.
proc realloc*(p: pointer, newsize: int): pointer {.noconv, rtl, tags: [].} proc alloc0*(T: typedesc, size = 1): ptr T {.inline.} =
## allocates a new memory block with at least ``T.sizeof * size``
## bytes. The block has to be freed with ``realloc(block, 0)`` or
## ``dealloc(block)``. The block is initialized with all bytes
## containing zero, so it is somewhat safer than ``alloc``.
## The allocated memory belongs to its allocating thread!
## Use `allocShared0` to allocate from a shared heap.
cast[ptr T](alloc0(T.sizeof * size))
proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [].}
## grows or shrinks a given memory block. If p is **nil** then a new ## grows or shrinks a given memory block. If p is **nil** then a new
## memory block is returned. In either way the block has at least ## memory block is returned. In either way the block has at least
## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil** ## ``newSize`` bytes. If ``newSize == 0`` and p is not **nil**
## ``realloc`` calls ``dealloc(p)``. In other cases the block has to ## ``realloc`` calls ``dealloc(p)``. In other cases the block has to
## be freed with ``dealloc``. ## be freed with ``dealloc``.
## The allocated memory belongs to its allocating thread! ## The allocated memory belongs to its allocating thread!
## Use `reallocShared` to reallocate from a shared heap. ## Use `reallocShared` to reallocate from a shared heap.
proc reallocType*[T](p: ptr T, newSize: int): ptr T {.inline.} =
## grows or shrinks a given memory block. If p is **nil** then a new
## memory block is returned. In either way the block has at least
## ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is not
## **nil** ``realloc`` calls ``dealloc(p)``. In other cases the block
## has to be freed with ``dealloc``. The allocated memory belongs to
## its allocating thread!
## Use `reallocShared` to reallocate from a shared heap.
cast[ptr T](realloc(p, T.sizeof * newSize))
proc dealloc*(p: pointer) {.noconv, rtl, tags: [].} proc dealloc*(p: pointer) {.noconv, rtl, tags: [].}
## frees the memory allocated with ``alloc``, ``alloc0`` or ## frees the memory allocated with ``alloc``, ``alloc0`` or
## ``realloc``. This procedure is dangerous! If one forgets to ## ``realloc``. This procedure is dangerous! If one forgets to
@ -1184,25 +1212,45 @@ when not defined(nimrodVM):
## or other memory may be corrupted. ## or other memory may be corrupted.
## The freed memory must belong to its allocating thread! ## The freed memory must belong to its allocating thread!
## Use `deallocShared` to deallocate from a shared heap. ## Use `deallocShared` to deallocate from a shared heap.
proc allocShared*(size: int): pointer {.noconv, rtl.} proc allocShared*(size: int): pointer {.noconv, rtl.}
## allocates a new memory block on the shared heap with at ## allocates a new memory block on the shared heap with at
## least ``size`` bytes. The block has to be freed with ## least ``size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block
## is not initialized, so reading from it before writing to it is ## is not initialized, so reading from it before writing to it is
## undefined behaviour! ## undefined behaviour!
proc allocShared*(T: typedesc, size: int): ptr T {.inline.} =
## allocates a new memory block on the shared heap with at
## least ``T.sizeof * size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block
## is not initialized, so reading from it before writing to it is
## undefined behaviour!
cast[ptr T](allocShared(T.sizeof * size))
proc allocShared0*(size: int): pointer {.noconv, rtl.} proc allocShared0*(size: int): pointer {.noconv, rtl.}
## allocates a new memory block on the shared heap with at ## allocates a new memory block on the shared heap with at
## least ``size`` bytes. The block has to be freed with ## least ``size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. ## ``reallocShared(block, 0)`` or ``deallocShared(block)``.
## The block is initialized with all bytes ## The block is initialized with all bytes
## containing zero, so it is somewhat safer than ``allocShared``. ## containing zero, so it is somewhat safer than ``allocShared``.
proc reallocShared*(p: pointer, newsize: int): pointer {.noconv, rtl.} proc allocShared0*(T: typedesc, size: int): ptr T {.inline.} =
## allocates a new memory block on the shared heap with at
## least ``T.sizeof * size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``.
## The block is initialized with all bytes
## containing zero, so it is somewhat safer than ``allocShared``.
cast[ptr T](allocShared(T.sizeof * size))
proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl.}
## grows or shrinks a given memory block on the heap. If p is **nil** ## grows or shrinks a given memory block on the heap. If p is **nil**
## then a new memory block is returned. In either way the block has at least ## then a new memory block is returned. In either way the block has at
## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil** ## least ``newSize`` bytes. If ``newSize == 0`` and p is not **nil**
## ``reallocShared`` calls ``deallocShared(p)``. In other cases the ## ``reallocShared`` calls ``deallocShared(p)``. In other cases the
## block has to be freed with ``deallocShared``. ## block has to be freed with ``deallocShared``.
proc reallocSharedType*[T](p: ptr T, newSize: int): ptr T {.inline.} =
## grows or shrinks a given memory block on the heap. If p is **nil**
## then a new memory block is returned. In either way the block has at
## least ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is
## not **nil** ``reallocShared`` calls ``deallocShared(p)``. In other
## cases the block has to be freed with ``deallocShared``.
cast[ptr T](reallocShared(p, T.sizeof * newSize))
proc deallocShared*(p: pointer) {.noconv, rtl.} proc deallocShared*(p: pointer) {.noconv, rtl.}
## frees the memory allocated with ``allocShared``, ``allocShared0`` or ## frees the memory allocated with ``allocShared``, ``allocShared0`` or
## ``reallocShared``. This procedure is dangerous! If one forgets to ## ``reallocShared``. This procedure is dangerous! If one forgets to

View file

@ -299,12 +299,12 @@ proc open*(address: string, server: bool, mode: TConnectionMode = conDEALER,
else: else:
if connect(result.s, address) != 0'i32: zmqError() if connect(result.s, address) != 0'i32: zmqError()
proc close*(c: var TConnection) = proc close*(c: TConnection) =
## closes the connection. ## closes the connection.
if close(c.s) != 0'i32: zmqError() if close(c.s) != 0'i32: zmqError()
if term(c.c) != 0'i32: zmqError() if term(c.c) != 0'i32: zmqError()
proc send*(c: var TConnection, msg: string) = proc send*(c: TConnection, msg: string) =
## sends a message over the connection. ## sends a message over the connection.
var m: TMsg var m: TMsg
if msg_init(m, msg.len) != 0'i32: zmqError() if msg_init(m, msg.len) != 0'i32: zmqError()
@ -312,7 +312,7 @@ proc send*(c: var TConnection, msg: string) =
if send(c.s, m, 0'i32) != 0'i32: zmqError() if send(c.s, m, 0'i32) != 0'i32: zmqError()
discard msg_close(m) discard msg_close(m)
proc receive*(c: var TConnection): string = proc receive*(c: TConnection): string =
## receives a message from a connection. ## receives a message from a connection.
var m: TMsg var m: TMsg
if msg_init(m) != 0'i32: zmqError() if msg_init(m) != 0'i32: zmqError()
@ -320,4 +320,3 @@ proc receive*(c: var TConnection): string =
result = newString(msg_size(m)) result = newString(msg_size(m))
copyMem(addr(result[0]), msg_data(m), result.len) copyMem(addr(result[0]), msg_data(m), result.len)
discard msg_close(m) discard msg_close(m)

View file

@ -33,7 +33,7 @@ If you are on a fairly modern *nix system, the following steps should work:
$ git clone git://github.com/Araq/Nimrod.git $ git clone git://github.com/Araq/Nimrod.git
$ cd Nimrod $ cd Nimrod
$ git clone --depth 1 git://github.com/nimrod-code/csources $ git clone --depth 1 git://github.com/nimrod-code/csources
$ cd csources && ./build.sh $ cd csources && sh build.sh
$ cd .. $ cd ..
$ bin/nimrod c koch $ bin/nimrod c koch
$ ./koch boot -d:release $ ./koch boot -d:release

View file

@ -24,3 +24,5 @@ ok supports(`+`, 34)
no compiles(4+5.0 * "hallo") no compiles(4+5.0 * "hallo")
no compiles(undeclaredIdentifier)
no compiles(undeclaredIdentifier)

View file

@ -11,3 +11,19 @@ proc q[T](x, y: T): T {.discardable.} =
p(8, 2) p(8, 2)
q[float](0.8, 0.2) q[float](0.8, 0.2)
# bug #942
template maybeMod(x: Tinteger, module:Natural):expr =
if module > 0: x mod module
else: x
proc foo(b: int):int =
var x = 1
result = x.maybeMod(b) # Works fine
proc bar(b: int):int =
result = 1
result = result.maybeMod(b) # Error: value returned by statement has to be discarded
echo foo(0)
echo bar(0)

45
tests/system/alloc.nim Normal file
View file

@ -0,0 +1,45 @@
var x: ptr int
x = cast[ptr int](alloc(7))
assert x != nil
x = alloc(int, 3)
assert x != nil
x.dealloc()
x = alloc0(int, 4)
assert cast[ptr array[4, int]](x)[0] == 0
assert cast[ptr array[4, int]](x)[1] == 0
assert cast[ptr array[4, int]](x)[2] == 0
assert cast[ptr array[4, int]](x)[3] == 0
x = cast[ptr int](x.realloc(2))
assert x != nil
x = x.reallocType(4)
assert x != nil
x.dealloc()
x = cast[ptr int](allocShared(100))
assert x != nil
deallocShared(x)
x = allocShared(int, 3)
assert x != nil
x.deallocShared()
x = allocShared0(int, 3)
assert x != nil
assert cast[ptr array[3, int]](x)[0] == 0
assert cast[ptr array[3, int]](x)[1] == 0
assert cast[ptr array[3, int]](x)[2] == 0
x = cast[ptr int](reallocShared(x, 2))
assert x != nil
x = reallocType(x, 12)
assert x != nil
x = reallocSharedType(x, 1)
assert x != nil
x.deallocShared()

13
tests/vm/twrongwhen.nim Normal file
View file

@ -0,0 +1,13 @@
discard """
output: "Error: cannot evaluate at compile time: x"
line: 7
"""
proc bla(x:int) =
when x == 0:
echo "oops"
else:
echo "good"
bla(2) # echos "oops"

View file

@ -27,7 +27,7 @@ version 0.9.x
============= =============
- memory manager: add a measure of fragmentation - memory manager: add a measure of fragmentation
- implement 'union' and 'bits' pragmas - implement 'bits' pragmas
- fix closures/lambdalifting - fix closures/lambdalifting
- ensure (ref T)(a, b) works as a type conversion and type constructor - ensure (ref T)(a, b) works as a type conversion and type constructor
- optimize 'genericReset'; 'newException' leads to code bloat - optimize 'genericReset'; 'newException' leads to code bloat

View file

@ -60,6 +60,9 @@ News
virtual machine. This fixes numerous bugs for ``nimrod i`` and for macro virtual machine. This fixes numerous bugs for ``nimrod i`` and for macro
evaluation. evaluation.
- ``--gc:none`` produces warnings when code uses the GC. - ``--gc:none`` produces warnings when code uses the GC.
- A ``union`` pragma for better C interoperability is now supported.
- Arrays can be annotated to be ``unchecked`` for easier low level
manipulations of memory.
Language Additions Language Additions