diff options
-rw-r--r-- | compiler/ast.nim | 2 | ||||
-rw-r--r-- | compiler/c2nim/cparse.nim | 21 | ||||
-rw-r--r-- | compiler/canonicalizer.nim | 7 | ||||
-rw-r--r-- | compiler/ccgexprs.nim | 2 | ||||
-rw-r--r-- | compiler/ccgtypes.nim | 44 | ||||
-rw-r--r-- | compiler/condsyms.nim | 1 | ||||
-rw-r--r-- | compiler/lookups.nim | 2 | ||||
-rw-r--r-- | compiler/pragmas.nim | 12 | ||||
-rw-r--r-- | compiler/semstmts.nim | 2 | ||||
-rw-r--r-- | compiler/vmdef.nim | 1 | ||||
-rw-r--r-- | compiler/vmgen.nim | 31 | ||||
-rw-r--r-- | compiler/wordrecg.nim | 4 | ||||
-rw-r--r-- | doc/manual.txt | 47 | ||||
-rw-r--r-- | lib/nimbase.h | 7 | ||||
-rw-r--r-- | lib/system.nim | 62 | ||||
-rw-r--r-- | lib/wrappers/zmq.nim | 7 | ||||
-rw-r--r-- | readme.md | 2 | ||||
-rw-r--r-- | tests/compiles/tcompiles.nim | 2 | ||||
-rw-r--r-- | tests/discard/tdiscardable.nim | 16 | ||||
-rw-r--r-- | tests/system/alloc.nim | 45 | ||||
-rw-r--r-- | tests/vm/twrongwhen.nim | 13 | ||||
-rw-r--r-- | todo.txt | 2 | ||||
-rw-r--r-- | web/news.txt | 3 |
23 files changed, 280 insertions, 55 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index eef12aebc..9c9dfce9a 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -489,6 +489,8 @@ const routineKinds* = {skProc, skMethod, skIterator, skClosureIterator, skConverter, skMacro, skTemplate} tfIncompleteStruct* = tfVarargs + tfUncheckedArray* = tfVarargs + tfUnion* = tfNoSideEffect skError* = skUnknown # type flags that are essential for type equality: diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim index e4abe11a0..555e1af10 100644 --- a/compiler/c2nim/cparse.nim +++ b/compiler/c2nim/cparse.nim @@ -681,11 +681,6 @@ proc parseField(p: var TParser, kind: TNodeKind): PNode = else: result = mangledIdent(p.tok.s, p) 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, kind: TNodeKind = nkRecList): PNode = result = newNodeP(kind, p) @@ -698,8 +693,7 @@ proc parseStructBody(p: var TParser, isUnion: bool, var i = parseField(p, kind) t = parseTypeSuffix(p, t) 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 getTok(p, def) eat(p, pxSemicolon, lastSon(result)) @@ -710,11 +704,12 @@ proc structPragmas(p: TParser, name: PNode, origName: string): PNode = result = newNodeP(nkPragmaExpr, p) addSon(result, exportSym(p, name, origName)) 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: addSon(pragmas, newIdentStrLitPair("importc", origName, 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 = result = newNodeP(nkPragmaExpr, p) @@ -726,9 +721,13 @@ proc enumPragmas(p: TParser, name: PNode): PNode = addSon(pragmas, e) addSon(result, pragmas) -proc parseStruct(p: var TParser, isUnion: bool): PNode = +proc parseStruct(p: var TParser, isUnion: bool): PNode = 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: addSon(result, parseStructBody(p, isUnion)) else: diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim index 94cb8e355..07e932b28 100644 --- a/compiler/canonicalizer.nim +++ b/compiler/canonicalizer.nim @@ -161,6 +161,13 @@ proc hashType(c: var MD5Context, t: PType) = if tfShared in t.flags: c &= "shared" 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 var c: MD5Context md5Init(c) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 2f88ecab5..a4ba412a4 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -736,7 +736,7 @@ proc genArrayElem(p: BProc, e: PNode, d: var TLoc) = var ty = skipTypes(skipTypes(a.t, abstractVarRange), abstractPtrs) var first = intLiteral(firstOrd(ty)) # emit range check: - if (optBoundsCheck in p.options): + if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags: if not isConstExpr(e.sons[1]): # semantic pass has already checked for const index expressions if firstOrd(ty) == 0: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index c856c6df0..321462044 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -376,10 +376,13 @@ proc getTypePre(m: BModule, typ: PType): PRope = else: result = getSimpleTypeDesc(m, 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 = - if gCmd == cmdCompileToCpp: result = "struct $1;$n" - else: result = "typedef struct $1 $1;$n" + if gCmd == cmdCompileToCpp: result = "$1 $2;$n" + else: result = "typedef $1 $2 $2;$n" proc getTypeForward(m: BModule, typ: PType): PRope = result = cacheGetType(m.forwTypeCache, typ) @@ -390,7 +393,8 @@ proc getTypeForward(m: BModule, typ: PType): PRope = of tySequence, tyTuple, tyObject: result = getTypeName(typ) if not isImportedType(typ): - appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result]) + appf(m.s[cfsForwardTypes], getForwardStructFormat(), + [structOrUnion(typ), result]) idTablePut(m.forwTypeCache, typ, result) else: internalError("getTypeForward(" & $typ.kind & ')') @@ -445,7 +449,12 @@ proc genRecordFieldsAux(m: BModule, n: PNode, if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname]) else: ae = sname 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()") 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 = # declare the record: var hasField = false + let aStruct = structOrUnion(typ) if typ.kind == tyObject: if typ.sons[0] == nil: 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: - 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 elif gCmd == cmdCompileToCpp: - result = ropecg(m, "struct $1 : public $2 {$n", - [name, getTypeDescAux(m, typ.sons[0], check)]) + result = ropecg(m, "$1 $2 : public $3 {$n", + [aStruct, name, getTypeDescAux(m, typ.sons[0], check)]) hasField = true else: - result = ropecg(m, "struct $1 {$n $2 Sup;$n", - [name, getTypeDescAux(m, typ.sons[0], check)]) + result = ropecg(m, "$1 $2 {$n $3 Sup;$n", + [aStruct, name, getTypeDescAux(m, typ.sons[0], check)]) hasField = true else: - result = ropef("struct $1 {$n", [name]) + result = ropef("$1 $2 {$n", [aStruct, name]) var desc = getRecordFields(m, typ, check) if (desc == nil) and not hasField: appf(result, "char dummy;$n", []) @@ -480,8 +490,8 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope, app(result, "};" & tnl) proc getTupleDesc(m: BModule, typ: PType, name: PRope, - check: var TIntSet): PRope = - result = ropef("struct $1 {$n", [name]) + check: var TIntSet): PRope = + result = ropef("$1 $2 {$n", [structOrUnion(typ), name]) var desc: PRope = nil for i in countup(0, sonsLen(typ) - 1): appf(desc, "$1 Field$2;$n", @@ -557,7 +567,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope = if result == nil: result = getTypeName(t) if not isImportedType(t): - appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result]) + appf(m.s[cfsForwardTypes], getForwardStructFormat(), + [structOrUnion(t), result]) idTablePut(m.forwTypeCache, t, result) assert(cacheGetType(m.typeCache, t) == nil) idTablePut(m.typeCache, t, con(result, "*")) @@ -588,7 +599,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope = if result == nil: result = getTypeName(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.typeCache, t, result) # always call for sideeffects: if t.kind != tyTuple: recdesc = getRecordDesc(m, t, result, check) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index c79fda13e..ddf2075b4 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -47,6 +47,7 @@ proc initDefines*() = defineSymbol("nimeffects") defineSymbol("nimbabel") defineSymbol("nimcomputedgoto") + defineSymbol("nimunion") # add platform specific symbols: case targetCPU diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 18b47a7bb..8239f2a47 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -92,7 +92,7 @@ proc errorSym*(c: PContext, n: PNode): PSym = result.typ = errorType(c) incl(result.flags, sfDiscardable) # 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) type diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index e0123c658..076e99924 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -50,9 +50,9 @@ const wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame, wRaises, wTags} 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, - wInheritable, wGensym, wInject, wRequiresInit} + wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion} fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, wImportCpp, wImportObjC, wError} varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, @@ -718,6 +718,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, noVal(it) if sym.typ == nil: invalidPragma(it) 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: noVal(it) if sym.typ == nil: invalidPragma(it) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 83450aa07..a11386966 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1323,7 +1323,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]): voidContext = true 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 if not isEmptyType(n.typ): n.kind = nkStmtListExpr elif i != last or voidContext or c.inTypeClass > 0: diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 30beea29c..102fc3024 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -161,6 +161,7 @@ type PProc* = ref object blocks*: seq[TBlock] # blocks; temp data structure + sym*: PSym slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]] maxSlots*: int diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index b4d73c0aa..b1a751723 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -980,6 +980,25 @@ proc setSlot(c: PCtx; v: PSym) = kind: if v.kind == skLet: slotFixedLet else: slotFixedVar) 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) = case le.kind of nkBracketExpr: @@ -1007,6 +1026,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = c.freeTemp(tmp) of nkSym: let s = le.sym + checkCanEval(c, le) if s.isGlobal: withTemp(tmp, le.typ): 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.freeTemp(val) 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 s.kind in {skParam,skResult}) var dest: TRegister = s.position + ord(s.kind == skParam) @@ -1046,10 +1066,6 @@ proc importcSym(c: PCtx; info: TLineInfo; s: PSym) = localError(info, errGenerated, "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 genGlobalInit(c: PCtx; n: PNode; s: PSym) = @@ -1190,12 +1206,14 @@ proc genVarSection(c: PCtx; n: PNode) = setSlot(c, a[i].sym) # v = t[i] var v: TDest = -1 + checkCanEval(c, a[i]) genRdVar(c, a[i], v, {gfAddrOf}) c.gABC(n, opcWrObj, v, tmp, i) # XXX globals? c.freeTemp(tmp) elif a.sons[0].kind == nkSym: let s = a.sons[0].sym + checkCanEval(c, a.sons[0]) if s.isGlobal: if s.position == 0: 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 of nkSym: let s = n.sym + checkCanEval(c, n) case s.kind of skVar, skForVar, skTemp, skLet, skParam, skResult: genRdVar(c, n, dest, flags) @@ -1525,7 +1544,7 @@ proc genProc(c: PCtx; s: PSym): int = # procs easily: let body = s.getBody let procStart = c.xjmp(body, opcJmp, 0) - var p = PProc(blocks: @[]) + var p = PProc(blocks: @[], sym: s) let oldPrc = c.prc c.prc = p # iterate over the parameters and allocate space for them: diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 1c8e4516c..f4a0c59f4 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -63,7 +63,7 @@ type wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt, wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, wAsmNoStackFrame, - wImplicitStatic, wGlobal, wCodegenDecl, + wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wAuto, wBool, wCatch, wChar, wClass, wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast, @@ -145,7 +145,7 @@ const "subschar", "acyclic", "shallow", "unroll", "linearscanend", "computedgoto", "injectstmt", "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit", - "asmnostackframe", "implicitstatic", "global", "codegendecl", + "asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked", "auto", "bool", "catch", "char", "class", "const_cast", "default", "delete", "double", diff --git a/doc/manual.txt b/doc/manual.txt index 427fa2eb7..3c1f7b651 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -5320,6 +5320,53 @@ strings automatically: 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 ------------------------ With the `dynlib`:idx: pragma a procedure or a variable can be imported from diff --git a/lib/nimbase.h b/lib/nimbase.h index 19d161adf..1100e084b 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -285,8 +285,8 @@ static N_INLINE(NI32, float32ToInt32)(float x) { typedef struct TStringDesc* string; -/* declared size of a sequence: */ -#if defined(__GNUC__) +/* declared size of a sequence/variable length array: */ +#if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) # define SEQ_DECL_SIZE /* empty is correct! */ #else # define SEQ_DECL_SIZE 1000000 @@ -373,5 +373,8 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); } # define GC_GUARD #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]; #endif diff --git a/lib/system.nim b/lib/system.nim index 869658727..171c7b6b8 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -185,6 +185,8 @@ proc `..`*[T](b: T): TSlice[T] {.noSideEffect, inline.} = when not defined(niminheritable): {.pragma: inheritable.} +when not defined(nimunion): + {.pragma: unchecked.} const NoFakeVars* = defined(NimrodVM) ## true if the backend doesn't support \ ## "fake variables" like 'var EBADF {.importc.}: cint'. @@ -194,9 +196,10 @@ when not defined(JS): TGenericSeq {.compilerproc, pure, inheritable.} = object len, reserved: int PGenericSeq {.exportc.} = ptr TGenericSeq + UncheckedCharArray {.unchecked.} = array[0..100_000_000, char] # len and space without counting the terminating zero: NimStringDesc {.compilerproc, final.} = object of TGenericSeq - data: array[0..100_000_000, char] + data: UncheckedCharArray NimString = ptr NimStringDesc 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! ## The allocated memory belongs to its allocating thread! ## 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: [].} ## allocates a new memory block with at least ``size`` bytes. The ## 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``. ## The allocated memory belongs to its allocating thread! ## 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 ## 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 ## be freed with ``dealloc``. ## The allocated memory belongs to its allocating thread! ## 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: [].} ## frees the memory allocated with ``alloc``, ``alloc0`` or ## ``realloc``. This procedure is dangerous! If one forgets to @@ -1184,25 +1212,45 @@ when not defined(nimrodVM): ## or other memory may be corrupted. ## The freed memory must belong to its allocating thread! ## Use `deallocShared` to deallocate from a shared heap. - proc allocShared*(size: int): pointer {.noconv, rtl.} ## allocates a new memory block on the shared heap with at ## least ``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! + 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.} ## allocates a new memory block on the shared heap with at ## least ``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``. - 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** - ## then a new memory block is returned. In either way the block has at least - ## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil** + ## then a new memory block is returned. In either way the block has at + ## least ``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``. + 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.} ## frees the memory allocated with ``allocShared``, ``allocShared0`` or ## ``reallocShared``. This procedure is dangerous! If one forgets to diff --git a/lib/wrappers/zmq.nim b/lib/wrappers/zmq.nim index 4e658028e..9826ab813 100644 --- a/lib/wrappers/zmq.nim +++ b/lib/wrappers/zmq.nim @@ -299,12 +299,12 @@ proc open*(address: string, server: bool, mode: TConnectionMode = conDEALER, else: if connect(result.s, address) != 0'i32: zmqError() -proc close*(c: var TConnection) = +proc close*(c: TConnection) = ## closes the connection. if close(c.s) != 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. var m: TMsg 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() discard msg_close(m) -proc receive*(c: var TConnection): string = +proc receive*(c: TConnection): string = ## receives a message from a connection. var m: TMsg if msg_init(m) != 0'i32: zmqError() @@ -320,4 +320,3 @@ proc receive*(c: var TConnection): string = result = newString(msg_size(m)) copyMem(addr(result[0]), msg_data(m), result.len) discard msg_close(m) - diff --git a/readme.md b/readme.md index 3eaef0b35..38e04233f 100644 --- a/readme.md +++ b/readme.md @@ -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 $ cd Nimrod $ git clone --depth 1 git://github.com/nimrod-code/csources -$ cd csources && ./build.sh +$ cd csources && sh build.sh $ cd .. $ bin/nimrod c koch $ ./koch boot -d:release diff --git a/tests/compiles/tcompiles.nim b/tests/compiles/tcompiles.nim index d0fccdaff..b3d9c17ce 100644 --- a/tests/compiles/tcompiles.nim +++ b/tests/compiles/tcompiles.nim @@ -24,3 +24,5 @@ ok supports(`+`, 34) no compiles(4+5.0 * "hallo") +no compiles(undeclaredIdentifier) +no compiles(undeclaredIdentifier) diff --git a/tests/discard/tdiscardable.nim b/tests/discard/tdiscardable.nim index c0551ba2f..a806ccdce 100644 --- a/tests/discard/tdiscardable.nim +++ b/tests/discard/tdiscardable.nim @@ -11,3 +11,19 @@ proc q[T](x, y: T): T {.discardable.} = p(8, 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) diff --git a/tests/system/alloc.nim b/tests/system/alloc.nim new file mode 100644 index 000000000..665b448ac --- /dev/null +++ b/tests/system/alloc.nim @@ -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() diff --git a/tests/vm/twrongwhen.nim b/tests/vm/twrongwhen.nim new file mode 100644 index 000000000..085bb6fb6 --- /dev/null +++ b/tests/vm/twrongwhen.nim @@ -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" + diff --git a/todo.txt b/todo.txt index ba062a949..a67ff5172 100644 --- a/todo.txt +++ b/todo.txt @@ -27,7 +27,7 @@ version 0.9.x ============= - memory manager: add a measure of fragmentation -- implement 'union' and 'bits' pragmas +- implement 'bits' pragmas - fix closures/lambdalifting - ensure (ref T)(a, b) works as a type conversion and type constructor - optimize 'genericReset'; 'newException' leads to code bloat diff --git a/web/news.txt b/web/news.txt index f9ac1b66d..0001cece7 100644 --- a/web/news.txt +++ b/web/news.txt @@ -60,6 +60,9 @@ News virtual machine. This fixes numerous bugs for ``nimrod i`` and for macro evaluation. - ``--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 |