summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim2
-rw-r--r--compiler/c2nim/cparse.nim21
-rw-r--r--compiler/canonicalizer.nim7
-rw-r--r--compiler/ccgexprs.nim2
-rw-r--r--compiler/ccgtypes.nim44
-rw-r--r--compiler/condsyms.nim1
-rw-r--r--compiler/lookups.nim2
-rw-r--r--compiler/pragmas.nim12
-rw-r--r--compiler/semstmts.nim2
-rw-r--r--compiler/vmdef.nim1
-rw-r--r--compiler/vmgen.nim31
-rw-r--r--compiler/wordrecg.nim4
-rw-r--r--doc/manual.txt47
-rw-r--r--lib/nimbase.h7
-rw-r--r--lib/system.nim62
-rw-r--r--lib/wrappers/zmq.nim7
-rw-r--r--readme.md2
-rw-r--r--tests/compiles/tcompiles.nim2
-rw-r--r--tests/discard/tdiscardable.nim16
-rw-r--r--tests/system/alloc.nim45
-rw-r--r--tests/vm/twrongwhen.nim13
-rw-r--r--todo.txt2
-rw-r--r--web/news.txt3
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