summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/condsyms.nim1
-rw-r--r--compiler/sempass2.nim12
-rwxr-xr-xcompiler/types.nim11
-rw-r--r--lib/core/locks.nim13
-rw-r--r--lib/impure/db_mongo.nim38
-rwxr-xr-xlib/impure/db_mysql.nim46
-rwxr-xr-xlib/impure/db_postgres.nim38
-rwxr-xr-xlib/impure/db_sqlite.nim42
-rwxr-xr-xlib/impure/rdstdin.nim12
-rwxr-xr-xlib/impure/web.nim2
-rwxr-xr-xlib/pure/osproc.nim35
-rwxr-xr-xlib/pure/streams.nim15
-rwxr-xr-xlib/system.nim98
-rwxr-xr-xlib/system/sysio.nim24
-rwxr-xr-xtodo.txt2
-rwxr-xr-xweb/question.txt14
16 files changed, 242 insertions, 161 deletions
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index f8f3c9dee..234029ea9 100755
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -59,6 +59,7 @@ proc InitDefines*() =
   DefineSymbol("nimhygiene")
   DefineSymbol("niminheritable")
   DefineSymbol("nimmixin")
+  DefineSymbol("nimeffects")
   
   # add platform specific symbols:
   case targetCPU
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index 59ae26385..c3e2ce8bc 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -102,7 +102,8 @@ proc addEffect(a: PEffects, e: PNode, useLineInfo=true) =
   throws(a.exc, e)
 
 proc mergeEffects(a: PEffects, b: PNode, useLineInfo: bool) =
-  for effect in items(b): addEffect(a, effect, useLineInfo)
+  if not b.isNil:
+    for effect in items(b): addEffect(a, effect, useLineInfo)
 
 proc addTag(a: PEffects, e: PNode, useLineInfo=true) =
   var aa = a.tags
@@ -113,7 +114,8 @@ proc addTag(a: PEffects, e: PNode, useLineInfo=true) =
   throws(a.tags, e)
 
 proc mergeTags(a: PEffects, b: PNode, useLineInfo: bool) =
-  for effect in items(b): addTag(a, effect, useLineInfo)
+  if not b.isNil:
+    for effect in items(b): addTag(a, effect, useLineInfo)
 
 proc listEffects(a: PEffects) =
   for e in items(a.exc):  Message(e.info, hintUser, typeToString(e.typ))
@@ -125,7 +127,7 @@ proc catches(tracked: PEffects, e: PType) =
   var i = tracked.bottom
   while i < L:
     # r supertype of e?
-    if inheritanceDiff(tracked.exc[i].excType, e) <= 0:
+    if safeInheritanceDiff(tracked.exc[i].excType, e) <= 0:
       tracked.exc.sons[i] = tracked.exc.sons[L-1]
       dec L
     else:
@@ -272,7 +274,7 @@ proc checkRaisesSpec(spec, real: PNode, msg: string, hints: bool) =
   for r in items(real):
     block search:
       for s in 0 .. <spec.len:
-        if inheritanceDiff(r.excType, spec[s].typ) <= 0:
+        if safeInheritanceDiff(r.excType, spec[s].typ) <= 0:
           used.incl(s)
           break search
       # XXX call graph analysis would be nice here!
@@ -341,6 +343,6 @@ proc trackProc*(s: PSym, body: PNode) =
   let tagsSpec = effectSpec(p, wTags)
   if not isNil(tagsSpec):
     checkRaisesSpec(tagsSpec, t.tags, "can have an unlisted effect: ",
-                    hints=on)
+                    hints=off)
     # after the check, use the formal spec:
     effects.sons[tagEffects] = tagsSpec
diff --git a/compiler/types.nim b/compiler/types.nim
index c994e0dd8..afc05d773 100755
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -1190,16 +1190,23 @@ proc baseOfDistinct*(t: PType): PType =
       internalAssert parent != nil
       parent.sons[0] = it.sons[0]
 
+proc safeInheritanceDiff*(a, b: PType): int =
+  # same as inheritanceDiff but checks for tyError:
+  if a.kind == tyError or b.kind == tyError: 
+    result = -1
+  else:
+    result = inheritanceDiff(a, b)
+
 proc compatibleEffectsAux(se, re: PNode): bool =
   if re.isNil: return false
   for r in items(re):
     block search:
       for s in items(se):
-        if inheritanceDiff(r.typ, s.typ) <= 0:
+        if safeInheritanceDiff(r.typ, s.typ) <= 0:
           break search
       return false
   result = true
- 
+
 proc compatibleEffects*(formal, actual: PType): bool =
   # for proc type compatibility checking:
   assert formal.kind == tyProc and actual.kind == tyProc
diff --git a/lib/core/locks.nim b/lib/core/locks.nim
index 083cc18d7..071bde93a 100644
--- a/lib/core/locks.nim
+++ b/lib/core/locks.nim
@@ -23,6 +23,13 @@ type
                     ## in preventDeadlocks-mode guarantees re-entrancy.
   TCond* = TSysCond ## Nimrod condition variable
   
+  FLock* = object of TEffect ## effect that denotes that some lock operation
+                             ## is performed
+  FAquireLock* = object of FLock  ## effect that denotes that some lock is
+                                  ## aquired
+  FReleaseLock* = object of FLock ## effect that denotes that some lock is
+                                  ## released
+  
 const
   noDeadlocks = defined(preventDeadlocks)
   maxLocksPerThread* = 10 ## max number of locks a thread can hold
@@ -51,7 +58,7 @@ proc DeinitLock*(lock: var TLock) {.inline.} =
   ## Frees the resources associated with the lock.
   DeinitSys(lock)
 
-proc TryAcquire*(lock: var TLock): bool = 
+proc TryAcquire*(lock: var TLock): bool {.tags: [FAquireLock].} = 
   ## Tries to acquire the given lock. Returns `true` on success.
   result = TryAcquireSys(lock)
   when noDeadlocks:
@@ -83,7 +90,7 @@ proc TryAcquire*(lock: var TLock): bool =
     inc(locksLen)
     assert OrderedLocks()
 
-proc Acquire*(lock: var TLock) =
+proc Acquire*(lock: var TLock) {.tags: [FAquireLock].} =
   ## Acquires the given lock.
   when nodeadlocks:
     var p = addr(lock)
@@ -128,7 +135,7 @@ proc Acquire*(lock: var TLock) =
   else:
     AcquireSys(lock)
   
-proc Release*(lock: var TLock) =
+proc Release*(lock: var TLock) {.tags: [FReleaseLock].} =
   ## Releases the given lock.
   when nodeadlocks:
     var p = addr(lock)
diff --git a/lib/impure/db_mongo.nim b/lib/impure/db_mongo.nim
index 5195e2a09..b7fb325f9 100644
--- a/lib/impure/db_mongo.nim
+++ b/lib/impure/db_mongo.nim
@@ -33,6 +33,10 @@ type
   EDb* = object of EIO ## exception that is raised if a database error occurs
   TDbConn* = TMongo    ## a database connection; alias for ``TMongo``
 
+  FDb* = object of FIO ## effect that denotes a database operation
+  FReadDb* = object of FReadIO   ## effect that denotes a read operation
+  FWriteDb* = object of FWriteIO ## effect that denotes a write operation
+
 proc dbError*(db: TDbConn, msg: string) {.noreturn.} = 
   ## raises an EDb exception with message `msg`.
   var e: ref EDb
@@ -43,12 +47,13 @@ proc dbError*(db: TDbConn, msg: string) {.noreturn.} =
     e.msg = $db.err & " " & msg
   raise e
 
-proc Close*(db: var TDbConn) = 
+proc Close*(db: var TDbConn) {.tags: [FDB].} = 
   ## closes the database connection.
   disconnect(db)
   destroy(db)
 
-proc Open*(host: string = defaultHost, port: int = defaultPort): TDbConn =
+proc Open*(host: string = defaultHost, port: int = defaultPort): TDbConn {.
+  tags: [FDB].} =
   ## opens a database connection. Raises `EDb` if the connection could not
   ## be established.
   init(result)
@@ -108,7 +113,8 @@ proc getId*(obj: var TBSon): TOid =
   else:
     raise newException(EInvalidIndex, "_id not in object")
 
-proc insertID*(db: var TDbConn, namespace: string, data: PJsonNode): TOid =
+proc insertID*(db: var TDbConn, namespace: string, data: PJsonNode): TOid {.
+  tags: [FWriteDb].} =
   ## converts `data` to BSON format and inserts it in `namespace`. Returns
   ## the generated OID for the ``_id`` field.
   result = genOid()
@@ -116,11 +122,13 @@ proc insertID*(db: var TDbConn, namespace: string, data: PJsonNode): TOid =
   insert(db, namespace, x)
   destroy(x)
 
-proc insert*(db: var TDbConn, namespace: string, data: PJsonNode) =
+proc insert*(db: var TDbConn, namespace: string, data: PJsonNode) {.
+  tags: [FWriteDb].} =
   ## converts `data` to BSON format and inserts it in `namespace`.  
   discard InsertID(db, namespace, data)
 
-proc update*(db: var TDbConn, namespace: string, obj: var TBSon) =
+proc update*(db: var TDbConn, namespace: string, obj: var TBSon) {.
+  tags: [FReadDB, FWriteDb].} =
   ## updates `obj` in `namespace`.
   var cond: TBson
   init(cond)
@@ -129,13 +137,15 @@ proc update*(db: var TDbConn, namespace: string, obj: var TBSon) =
   update(db, namespace, cond, obj, ord(UPDATE_UPSERT))
   destroy(cond)
 
-proc update*(db: var TDbConn, namespace: string, oid: TOid, obj: PJsonNode) =
+proc update*(db: var TDbConn, namespace: string, oid: TOid, obj: PJsonNode) {.
+  tags: [FReadDB, FWriteDb].} =
   ## updates the data with `oid` to have the new data `obj`.
   var a = jsonToBSon(obj, oid)
   Update(db, namespace, a)
   destroy(a)
 
-proc delete*(db: var TDbConn, namespace: string, oid: TOid) =
+proc delete*(db: var TDbConn, namespace: string, oid: TOid) {.
+  tags: [FWriteDb].} =
   ## Deletes the object belonging to `oid`.
   var cond: TBson
   init(cond)
@@ -144,11 +154,13 @@ proc delete*(db: var TDbConn, namespace: string, oid: TOid) =
   discard remove(db, namespace, cond)
   destroy(cond)
 
-proc delete*(db: var TDbConn, namespace: string, obj: var TBSon) =
+proc delete*(db: var TDbConn, namespace: string, obj: var TBSon) {.
+  tags: [FWriteDb].} =
   ## Deletes the object `obj`.
   delete(db, namespace, getId(obj))
 
-iterator find*(db: var TDbConn, namespace: string): var TBSon =
+iterator find*(db: var TDbConn, namespace: string): var TBSon {.
+  tags: [FReadDB].} =
   ## iterates over any object in `namespace`.
   var cursor: TCursor
   init(cursor, db, namespace)
@@ -157,7 +169,7 @@ iterator find*(db: var TDbConn, namespace: string): var TBSon =
   destroy(cursor)
 
 iterator find*(db: var TDbConn, namespace: string, 
-               query, fields: var TBSon): var TBSon =
+               query, fields: var TBSon): var TBSon {.tags: [FReadDB].} =
   ## yields the `fields` of any document that suffices `query`.
   var cursor = find(db, namespace, query, fields, 0'i32, 0'i32, 0'i32)
   if cursor != nil:
@@ -171,7 +183,8 @@ proc setupFieldnames(fields: varargs[string]): TBSon =
   finish(result)
 
 iterator find*(db: var TDbConn, namespace: string, 
-               query: var TBSon, fields: varargs[string]): var TBSon =
+               query: var TBSon, fields: varargs[string]): var TBSon {.
+               tags: [FReadDB].} =
   ## yields the `fields` of any document that suffices `query`. If `fields` 
   ## is ``[]`` the whole document is yielded.
   var f = setupFieldnames(fields)
@@ -188,7 +201,8 @@ proc setupQuery(query: string): TBSon =
   finish(result)
 
 iterator find*(db: var TDbConn, namespace: string, 
-               query: string, fields: varargs[string]): var TBSon =
+               query: string, fields: varargs[string]): var TBSon {.
+               tags: [FReadDB].} =
   ## yields the `fields` of any document that suffices `query`. If `fields` 
   ## is ``[]`` the whole document is yielded.
   var f = setupFieldnames(fields)
diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim
index 6284b7995..118ed39bb 100755
--- a/lib/impure/db_mysql.nim
+++ b/lib/impure/db_mysql.nim
@@ -18,7 +18,11 @@ type
   EDb* = object of EIO ## exception that is raised if a database error occurs
 
   TSqlQuery* = distinct string ## an SQL query string
- 
+
+  FDb* = object of FIO ## effect that denotes a database operation
+  FReadDb* = object of FReadIO   ## effect that denotes a read operation
+  FWriteDb* = object of FWriteIO ## effect that denotes a write operation
+
 proc dbError(db: TDbConn) {.noreturn.} = 
   ## raises an EDb exception.
   var e: ref EDb
@@ -60,12 +64,18 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
     else: 
       add(result, c)
   
-proc TryExec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]): bool =
+proc TryExec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]): bool {.
+  tags: [FReadDB, FWriteDb].} =
   ## tries to execute the query and returns true if successful, false otherwise.
   var q = dbFormat(query, args)
   return mysql.RealQuery(db, q, q.len) == 0'i32
 
-proc Exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) =
+proc rawExec(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) =
+  var q = dbFormat(query, args)
+  if mysql.RealQuery(db, q, q.len) != 0'i32: dbError(db)
+
+proc Exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {.
+  tags: [FReadDB, FWriteDb].} =
   ## executes the query and raises EDB if not successful.
   var q = dbFormat(query, args)
   if mysql.RealQuery(db, q, q.len) != 0'i32: dbError(db)
@@ -80,11 +90,11 @@ proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
   mysql.FreeResult(sqlres)
   
 iterator FastRows*(db: TDbConn, query: TSqlQuery,
-                   args: varargs[string, `$`]): TRow =
+                   args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
   ## executes the query and iterates over the result dataset. This is very 
   ## fast, but potenially dangerous: If the for-loop-body executes another
   ## query, the results can be undefined. For MySQL this is the case!.
-  Exec(db, query, args)
+  rawExec(db, query, args)
   var sqlres = mysql.UseResult(db)
   if sqlres != nil:
     var L = int(mysql.NumFields(sqlres))
@@ -100,9 +110,9 @@ iterator FastRows*(db: TDbConn, query: TSqlQuery,
     properFreeResult(sqlres, row)
 
 proc getRow*(db: TDbConn, query: TSqlQuery,
-             args: varargs[string, `$`]): TRow =
+             args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
   ## retrieves a single row.
-  Exec(db, query, args)
+  rawExec(db, query, args)
   var sqlres = mysql.UseResult(db)
   if sqlres != nil:
     var L = int(mysql.NumFields(sqlres))
@@ -115,10 +125,10 @@ proc getRow*(db: TDbConn, query: TSqlQuery,
     properFreeResult(sqlres, row)
 
 proc GetAllRows*(db: TDbConn, query: TSqlQuery, 
-                 args: varargs[string, `$`]): seq[TRow] =
+                 args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} =
   ## executes the query and returns the whole result dataset.
   result = @[]
-  Exec(db, query, args)
+  rawExec(db, query, args)
   var sqlres = mysql.UseResult(db)
   if sqlres != nil:
     var L = int(mysql.NumFields(sqlres))
@@ -134,12 +144,12 @@ proc GetAllRows*(db: TDbConn, query: TSqlQuery,
     mysql.FreeResult(sqlres)
 
 iterator Rows*(db: TDbConn, query: TSqlQuery, 
-               args: varargs[string, `$`]): TRow =
+               args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
   ## same as `FastRows`, but slower and safe.
   for r in items(GetAllRows(db, query, args)): yield r
 
 proc GetValue*(db: TDbConn, query: TSqlQuery, 
-               args: varargs[string, `$`]): string = 
+               args: varargs[string, `$`]): string {.tags: [FReadDB].} = 
   ## executes the query and returns the result dataset's the first column 
   ## of the first row. Returns "" if the dataset contains no rows. This uses
   ## `FastRows`, so it inherits its fragile behaviour.
@@ -149,7 +159,7 @@ proc GetValue*(db: TDbConn, query: TSqlQuery,
     break
 
 proc TryInsertID*(db: TDbConn, query: TSqlQuery, 
-                  args: varargs[string, `$`]): int64 =
+                  args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
   ## executes the query (typically "INSERT") and returns the 
   ## generated ID for the row or -1 in case of an error.
   var q = dbFormat(query, args)
@@ -159,24 +169,26 @@ proc TryInsertID*(db: TDbConn, query: TSqlQuery,
     result = mysql.InsertId(db)
   
 proc InsertID*(db: TDbConn, query: TSqlQuery, 
-               args: varargs[string, `$`]): int64 = 
+               args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} = 
   ## executes the query (typically "INSERT") and returns the 
   ## generated ID for the row.
   result = TryInsertID(db, query, args)
   if result < 0: dbError(db)
 
 proc ExecAffectedRows*(db: TDbConn, query: TSqlQuery, 
-                       args: varargs[string, `$`]): int64 = 
+                       args: varargs[string, `$`]): int64 {.
+                       tags: [FReadDB, FWriteDb].} = 
   ## runs the query (typically "UPDATE") and returns the
   ## number of affected rows
-  Exec(db, query, args)
+  rawExec(db, query, args)
   result = mysql.AffectedRows(db)
 
-proc Close*(db: TDbConn) = 
+proc Close*(db: TDbConn) {.tags: [FDb].} = 
   ## closes the database connection.
   if db != nil: mysql.Close(db)
 
-proc Open*(connection, user, password, database: string): TDbConn =
+proc Open*(connection, user, password, database: string): TDbConn {.
+  tags: [FDb].} =
   ## opens a database connection. Raises `EDb` if the connection could not
   ## be established.
   result = mysql.Init(nil)
diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim
index cb8da75b8..2e2b09bc2 100755
--- a/lib/impure/db_postgres.nim
+++ b/lib/impure/db_postgres.nim
@@ -18,6 +18,10 @@ type
   EDb* = object of EIO ## exception that is raised if a database error occurs
   
   TSqlQuery* = distinct string ## an SQL query string
+
+  FDb* = object of FIO ## effect that denotes a database operation
+  FReadDb* = object of FReadIO   ## effect that denotes a read operation
+  FWriteDb* = object of FWriteIO ## effect that denotes a write operation
   
 proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} =  
   ## constructs a TSqlQuery from the string `query`. This is supposed to be 
@@ -60,14 +64,15 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
       add(result, c)
   
 proc TryExec*(db: TDbConn, query: TSqlQuery, 
-              args: varargs[string, `$`]): bool =
+              args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
   ## tries to execute the query and returns true if successful, false otherwise.
   var q = dbFormat(query, args)
   var res = PQExec(db, q)
   result = PQresultStatus(res) == PGRES_COMMAND_OK
   PQclear(res)
 
-proc Exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) =
+proc Exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {.
+  tags: [FReadDB, FWriteDb].} =
   ## executes the query and raises EDB if not successful.
   var q = dbFormat(query, args)
   var res = PQExec(db, q)
@@ -91,7 +96,7 @@ proc setRow(res: PPGresult, r: var TRow, line, cols: int32) =
     add(r[col], x)
   
 iterator FastRows*(db: TDbConn, query: TSqlQuery,
-                   args: varargs[string, `$`]): TRow =
+                   args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
   ## executes the query and iterates over the result dataset. This is very 
   ## fast, but potenially dangerous: If the for-loop-body executes another
   ## query, the results can be undefined. For Postgres it is safe though.
@@ -104,7 +109,7 @@ iterator FastRows*(db: TDbConn, query: TSqlQuery,
   PQclear(res)
 
 proc getRow*(db: TDbConn, query: TSqlQuery,
-             args: varargs[string, `$`]): TRow =
+             args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
   ## retrieves a single row.
   var res = setupQuery(db, query, args)
   var L = PQnfields(res)
@@ -113,38 +118,39 @@ proc getRow*(db: TDbConn, query: TSqlQuery,
   PQclear(res)
 
 proc GetAllRows*(db: TDbConn, query: TSqlQuery, 
-                 args: varargs[string, `$`]): seq[TRow] =
+                 args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} =
   ## executes the query and returns the whole result dataset.
   result = @[]
   for r in FastRows(db, query, args):
     result.add(r)
 
 iterator Rows*(db: TDbConn, query: TSqlQuery, 
-               args: varargs[string, `$`]): TRow =
+               args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
   ## same as `FastRows`, but slower and safe.
   for r in items(GetAllRows(db, query, args)): yield r
 
 proc GetValue*(db: TDbConn, query: TSqlQuery, 
-               args: varargs[string, `$`]): string = 
+               args: varargs[string, `$`]): string {.tags: [FReadDB].} = 
   ## executes the query and returns the result dataset's the first column 
   ## of the first row. Returns "" if the dataset contains no rows.
   var x = PQgetvalue(setupQuery(db, query, args), 0, 0)
   result = if isNil(x): "" else: $x
   
 proc TryInsertID*(db: TDbConn, query: TSqlQuery, 
-                  args: varargs[string, `$`]): int64 =
+                  args: varargs[string, `$`]): int64  {.tags: [FWriteDb].}=
   ## executes the query (typically "INSERT") and returns the 
   ## generated ID for the row or -1 in case of an error. For Postgre this adds
   ## ``RETURNING id`` to the query, so it only works if your primary key is
   ## named ``id``. 
-  var val = GetValue(db, TSqlQuery(string(query) & " RETURNING id"), args)
-  if val.len > 0:
-    result = ParseBiggestInt(val)
+  var x = PQgetvalue(setupQuery(db, TSqlQuery(string(query) & " RETURNING id"), 
+    args), 0, 0)
+  if not isNil(x):
+    result = ParseBiggestInt($x)
   else:
     result = -1
 
 proc InsertID*(db: TDbConn, query: TSqlQuery, 
-               args: varargs[string, `$`]): int64 = 
+               args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
   ## executes the query (typically "INSERT") and returns the 
   ## generated ID for the row. For Postgre this adds
   ## ``RETURNING id`` to the query, so it only works if your primary key is
@@ -153,7 +159,8 @@ proc InsertID*(db: TDbConn, query: TSqlQuery,
   if result < 0: dbError(db)
   
 proc ExecAffectedRows*(db: TDbConn, query: TSqlQuery, 
-                       args: varargs[string, `$`]): int64 = 
+                       args: varargs[string, `$`]): int64 {.tags: [
+                       FReadDB, FWriteDb].} = 
   ## executes the query (typically "UPDATE") and returns the
   ## number of affected rows.
   var q = dbFormat(query, args)
@@ -162,11 +169,12 @@ proc ExecAffectedRows*(db: TDbConn, query: TSqlQuery,
   result = parseBiggestInt($PQcmdTuples(res))
   PQclear(res)
 
-proc Close*(db: TDbConn) = 
+proc Close*(db: TDbConn) {.tags: [FDb].} = 
   ## closes the database connection.
   if db != nil: PQfinish(db)
 
-proc Open*(connection, user, password, database: string): TDbConn =
+proc Open*(connection, user, password, database: string): TDbConn {.
+  tags: [FDb].} =
   ## opens a database connection. Raises `EDb` if the connection could not
   ## be established.
   result = PQsetdbLogin(nil, nil, nil, nil, database, user, password)
diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim
index 4abc2ca97..e9864c599 100755
--- a/lib/impure/db_sqlite.nim
+++ b/lib/impure/db_sqlite.nim
@@ -19,6 +19,10 @@ type
   
   TSqlQuery* = distinct string ## an SQL query string
   
+  FDb* = object of FIO ## effect that denotes a database operation
+  FReadDb* = object of FReadIO   ## effect that denotes a read operation
+  FWriteDb* = object of FWriteIO ## effect that denotes a write operation
+  
 proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} =  
   ## constructs a TSqlQuery from the string `query`. This is supposed to be 
   ## used as a raw-string-literal modifier:
@@ -60,7 +64,7 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
       add(result, c)
   
 proc TryExec*(db: TDbConn, query: TSqlQuery, 
-              args: varargs[string, `$`]): bool =
+              args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
   ## tries to execute the query and returns true if successful, false otherwise.
   var q = dbFormat(query, args)
   var stmt: sqlite3.PStmt
@@ -68,7 +72,8 @@ proc TryExec*(db: TDbConn, query: TSqlQuery,
     if step(stmt) == SQLITE_DONE:
       result = finalize(stmt) == SQLITE_OK
 
-proc Exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) =
+proc Exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`])  {.
+  tags: [FReadDB, FWriteDb].} =
   ## executes the query and raises EDB if not successful.
   if not TryExec(db, query, args): dbError(db)
   
@@ -89,7 +94,7 @@ proc setRow(stmt: PStmt, r: var TRow, cols: cint) =
     if not isNil(x): add(r[col], x)
   
 iterator FastRows*(db: TDbConn, query: TSqlQuery,
-                   args: varargs[string, `$`]): TRow =
+                   args: varargs[string, `$`]): TRow  {.tags: [FReadDB].} =
   ## executes the query and iterates over the result dataset. This is very 
   ## fast, but potenially dangerous: If the for-loop-body executes another
   ## query, the results can be undefined. For Sqlite it is safe though.
@@ -102,7 +107,7 @@ iterator FastRows*(db: TDbConn, query: TSqlQuery,
   if finalize(stmt) != SQLITE_OK: dbError(db)
 
 proc getRow*(db: TDbConn, query: TSqlQuery,
-             args: varargs[string, `$`]): TRow =
+             args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
   ## retrieves a single row.
   var stmt = setupQuery(db, query, args)
   var L = (columnCount(stmt))
@@ -112,19 +117,19 @@ proc getRow*(db: TDbConn, query: TSqlQuery,
   if finalize(stmt) != SQLITE_OK: dbError(db)
 
 proc GetAllRows*(db: TDbConn, query: TSqlQuery, 
-                 args: varargs[string, `$`]): seq[TRow] =
+                 args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} =
   ## executes the query and returns the whole result dataset.
   result = @[]
   for r in FastRows(db, query, args):
     result.add(r)
 
 iterator Rows*(db: TDbConn, query: TSqlQuery, 
-               args: varargs[string, `$`]): TRow =
+               args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
   ## same as `FastRows`, but slower and safe.
   for r in FastRows(db, query, args): yield r
 
 proc GetValue*(db: TDbConn, query: TSqlQuery, 
-               args: varargs[string, `$`]): string = 
+               args: varargs[string, `$`]): string {.tags: [FReadDB].} = 
   ## executes the query and returns the result dataset's the first column 
   ## of the first row. Returns "" if the dataset contains no rows.
   var stmt = setupQuery(db, query, args)
@@ -140,16 +145,19 @@ proc GetValue*(db: TDbConn, query: TSqlQuery,
     result = ""
   
 proc TryInsertID*(db: TDbConn, query: TSqlQuery, 
-                  args: varargs[string, `$`]): int64 =
+                  args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
   ## executes the query (typically "INSERT") and returns the 
   ## generated ID for the row or -1 in case of an error. 
-  if tryExec(db, query, args): 
-    result = last_insert_rowid(db)
-  else:
-    result = -1
+  var q = dbFormat(query, args)
+  var stmt: sqlite3.PStmt
+  if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK:
+    if step(stmt) == SQLITE_DONE:
+      if finalize(stmt) == SQLITE_OK:
+        return last_insert_rowid(db)
+  result = -1
 
 proc InsertID*(db: TDbConn, query: TSqlQuery, 
-               args: varargs[string, `$`]): int64 = 
+               args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} = 
   ## executes the query (typically "INSERT") and returns the 
   ## generated ID for the row. For Postgre this adds
   ## ``RETURNING id`` to the query, so it only works if your primary key is
@@ -158,17 +166,19 @@ proc InsertID*(db: TDbConn, query: TSqlQuery,
   if result < 0: dbError(db)
   
 proc ExecAffectedRows*(db: TDbConn, query: TSqlQuery, 
-                       args: varargs[string, `$`]): int64 = 
+                       args: varargs[string, `$`]): int64 {.
+                       tags: [FReadDB, FWriteDb].} = 
   ## executes the query (typically "UPDATE") and returns the
   ## number of affected rows.
   Exec(db, query, args)
   result = changes(db)
 
-proc Close*(db: TDbConn) = 
+proc Close*(db: TDbConn) {.tags: [FDB].} = 
   ## closes the database connection.
   if sqlite3.close(db) != SQLITE_OK: dbError(db)
     
-proc Open*(connection, user, password, database: string): TDbConn =
+proc Open*(connection, user, password, database: string): TDbConn {.
+  tags: [FDB].} =
   ## opens a database connection. Raises `EDb` if the connection could not
   ## be established. Only the ``connection`` parameter is used for ``sqlite``.
   var db: TDbConn
diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim
index cd614fa98..cf076e929 100755
--- a/lib/impure/rdstdin.nim
+++ b/lib/impure/rdstdin.nim
@@ -14,12 +14,14 @@
 ## wanted functionality.
 
 when defined(Windows):
-  proc ReadLineFromStdin*(prompt: string): TaintedString = 
+  proc ReadLineFromStdin*(prompt: string): TaintedString {.
+                          tags: [FReadIO, FWriteIO].} = 
     ## Reads a line from stdin.
     stdout.write(prompt)
     result = readLine(stdin)
 
-  proc ReadLineFromStdin*(prompt: string, line: var TaintedString): bool =
+  proc ReadLineFromStdin*(prompt: string, line: var TaintedString): bool {.
+                          tags: [FReadIO, FWriteIO].} =
     ## Reads a `line` from stdin. `line` must not be
     ## ``nil``! May throw an IO exception.
     ## A line of text may be delimited by ``CR``, ``LF`` or
@@ -32,7 +34,8 @@ when defined(Windows):
 else:
   import readline, history
     
-  proc ReadLineFromStdin*(prompt: string): TaintedString = 
+  proc ReadLineFromStdin*(prompt: string): TaintedString {.
+                          tags: [FReadIO, FWriteIO].} =
     var buffer = readline.readLine(prompt)
     if isNil(buffer): quit(0)
     result = TaintedString($buffer)
@@ -40,7 +43,8 @@ else:
       add_history(buffer)
     readline.free(buffer)
 
-  proc ReadLineFromStdin*(prompt: string, line: var TaintedString): bool =
+  proc ReadLineFromStdin*(prompt: string, line: var TaintedString): bool {.
+                          tags: [FReadIO, FWriteIO].} =
     var buffer = readline.readLine(prompt)
     if isNil(buffer): quit(0)
     line = TaintedString($buffer)
diff --git a/lib/impure/web.nim b/lib/impure/web.nim
index a0aea8421..417fe9746 100755
--- a/lib/impure/web.nim
+++ b/lib/impure/web.nim
@@ -58,5 +58,5 @@ proc URLretrieveString*(url: string): TaintedString =
   result = stream.data.TaintedString

 

 when isMainModule:

-  echo URLretrieveString("http://nimrod.ethexor.com/")

+  echo URLretrieveString("http://nimrod-code.org/")

 

diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index b9dee638d..46b35cb10 100755
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -43,11 +43,12 @@ type
 proc execProcess*(command: string,
                   options: set[TProcessOption] = {poStdErrToStdOut,
                                                   poUseShell}): TaintedString {.
-                                                  rtl, extern: "nosp$1".}
+                                                  rtl, extern: "nosp$1",
+                                                  tags: [FExecIO, FReadIO].}
   ## A convenience procedure that executes ``command`` with ``startProcess``
   ## and returns its output as a string.
 
-proc execCmd*(command: string): int {.rtl, extern: "nosp$1".}
+proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [FExecIO].}
   ## Executes ``command`` and returns its error code. Standard input, output,
   ## error streams are inherited from the calling process. This operation
   ## is also often called `system`:idx:.
@@ -57,7 +58,7 @@ proc startProcess*(command: string,
                    args: openarray[string] = [],
                    env: PStringTable = nil, 
                    options: set[TProcessOption] = {poStdErrToStdOut}): 
-              PProcess {.rtl, extern: "nosp$1".}
+              PProcess {.rtl, extern: "nosp$1", tags: [FExecIO].}
   ## Starts a process. `Command` is the executable file, `workingDir` is the
   ## process's working directory. If ``workingDir == ""`` the current directory
   ## is used. `args` are the command line arguments that are passed to the
@@ -73,7 +74,7 @@ proc startProcess*(command: string,
   ## but ``EOS`` is raised in case of an error.
 
 proc startCmd*(command: string, options: set[TProcessOption] = {
-               poStdErrToStdOut, poUseShell}): PProcess =
+               poStdErrToStdOut, poUseShell}): PProcess {.tags: [FExecIO].} =
   ## a simpler version of `startProcess` that parses the command line into
   ## program and arguments and then calls `startProcess` with the empty string
   ## for `workingDir` and the nil string table for `env`.
@@ -83,38 +84,39 @@ proc startCmd*(command: string, options: set[TProcessOption] = {
   for i in 1 .. c.len-1: a[i-1] = c[i]
   result = startProcess(command=c[0], args=a, options=options)
 
-proc close*(p: PProcess) {.rtl, extern: "nosp$1".}
+proc close*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].}
   ## When the process has finished executing, cleanup related handles
 
-proc suspend*(p: PProcess) {.rtl, extern: "nosp$1".}
+proc suspend*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].}
   ## Suspends the process `p`.
 
-proc resume*(p: PProcess) {.rtl, extern: "nosp$1".}
+proc resume*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].}
   ## Resumes the process `p`.
 
-proc terminate*(p: PProcess) {.rtl, extern: "nosp$1".}
+proc terminate*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].}
   ## Terminates the process `p`.
 
-proc running*(p: PProcess): bool {.rtl, extern: "nosp$1".}
+proc running*(p: PProcess): bool {.rtl, extern: "nosp$1", tags: [].}
   ## Returns true iff the process `p` is still running. Returns immediately.
 
 proc processID*(p: PProcess): int {.rtl, extern: "nosp$1".} =
   ## returns `p`'s process ID.
   return p.id
 
-proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl, extern: "nosp$1".}
+proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl, 
+  extern: "nosp$1", tags: [].}
   ## waits for the process to finish and returns `p`'s error code.
 
-proc peekExitCode*(p: PProcess): int
+proc peekExitCode*(p: PProcess): int {.tags: [].}
   ## return -1 if the process is still running. Otherwise the process' exit code
 
-proc inputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1".}
+proc inputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
   ## opens ``p``'s input stream for writing to
 
-proc outputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1".}
+proc outputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
   ## opens ``p``'s output stream for reading from
 
-proc errorStream*(p: PProcess): PStream {.rtl, extern: "nosp$1".}
+proc errorStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
   ## opens ``p``'s output stream for reading from
 
 when defined(macosx) or defined(bsd):
@@ -156,7 +158,8 @@ proc countProcessors*(): int {.rtl, extern: "nosp$1".} =
 
 proc execProcesses*(cmds: openArray[string],
                     options = {poStdErrToStdOut, poParentStreams},
-                    n = countProcessors()): int {.rtl, extern: "nosp$1".} =
+                    n = countProcessors()): int {.rtl, extern: "nosp$1", 
+                    tags: [FExecIO].} =
   ## executes the commands `cmds` in parallel. Creates `n` processes
   ## that execute in parallel. The highest return value of all processes
   ## is returned.
@@ -731,7 +734,7 @@ elif not defined(useNimRtl):
 proc execCmdEx*(command: string, options: set[TProcessOption] = {
                 poStdErrToStdOut, poUseShell}): tuple[
                 output: TaintedString, 
-                exitCode: int] =
+                exitCode: int] {.tags: [FExecIO, FReadIO].} =
   ## a convenience proc that runs the `command`, grabs all its output and
   ## exit code and returns both.
   var p = startCmd(command, options)
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
index 4cd48af31..5db21d76a 100755
--- a/lib/pure/streams.nim
+++ b/lib/pure/streams.nim
@@ -23,14 +23,15 @@ type
                                ## here shouldn't be used directly. They are
                                ## accessible so that a stream implementation
                                ## can override them.
-    closeImpl*: proc (s: PStream) {.nimcall.}
-    atEndImpl*: proc (s: PStream): bool {.nimcall.}
-    setPositionImpl*: proc (s: PStream, pos: int) {.nimcall.}
-    getPositionImpl*: proc (s: PStream): int {.nimcall.}
+    closeImpl*: proc (s: PStream) {.nimcall, tags: [].}
+    atEndImpl*: proc (s: PStream): bool {.nimcall, tags: [].}
+    setPositionImpl*: proc (s: PStream, pos: int) {.nimcall, tags: [].}
+    getPositionImpl*: proc (s: PStream): int {.nimcall, tags: [].}
     readDataImpl*: proc (s: PStream, buffer: pointer,
-                         bufLen: int): int {.nimcall.}
-    writeDataImpl*: proc (s: PStream, buffer: pointer, bufLen: int) {.nimcall.}
-    flushImpl*: proc (s: PStream) {.nimcall.}
+                         bufLen: int): int {.nimcall, tags: [FReadIO].}
+    writeDataImpl*: proc (s: PStream, buffer: pointer, bufLen: int) {.nimcall,
+      tags: [FWriteIO].}
+    flushImpl*: proc (s: PStream) {.nimcall, tags: [FWriteIO].}
 
 proc flush*(s: PStream) =
   ## flushes the buffers that the stream `s` might use.
diff --git a/lib/system.nim b/lib/system.nim
index 6d7720aa7..d4c7aaf5e 100755
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -1080,21 +1080,21 @@ proc equalMem*(a, b: Pointer, size: int): bool {.
   ## otherwise. Like any procedure dealing with raw memory this is
   ## *unsafe*.
 
-proc alloc*(size: int): pointer {.noconv, rtl.}
+proc alloc*(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
   ## ``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.
-proc alloc0*(size: int): pointer {.noconv, rtl.}
+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
   ## ``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.
-proc realloc*(p: Pointer, newsize: int): pointer {.noconv, rtl.}
+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**
@@ -1102,7 +1102,7 @@ proc realloc*(p: Pointer, newsize: int): pointer {.noconv, rtl.}
   ## be freed with ``dealloc``.
   ## The allocated memory belongs to its allocating thread!
   ## Use `reallocShared` to reallocate from a shared heap.
-proc dealloc*(p: Pointer) {.noconv, rtl.}
+proc dealloc*(p: Pointer) {.noconv, rtl, tags: [].}
   ## frees the memory allocated with ``alloc``, ``alloc0`` or
   ## ``realloc``. This procedure is dangerous! If one forgets to
   ## free the memory a leak occurs; if one tries to access freed
@@ -1618,7 +1618,7 @@ var
     ## do when setting this. If ``localRaiseHook`` returns false, the exception
     ## is caught and does not propagate further through the call stack.
     
-  outOfMemHook*: proc () {.nimcall.}
+  outOfMemHook*: proc () {.nimcall, tags: [].}
     ## set this variable to provide a procedure that should be called 
     ## in case of an `out of memory`:idx: event. The standard handler
     ## writes an error message and terminates the program. `outOfMemHook` can
@@ -1668,7 +1668,7 @@ else:
 
   proc add*(x: var cstring, y: cstring) {.magic: "AppendStrStr".}
 
-proc echo*[T](x: varargs[T, `$`]) {.magic: "Echo".}
+proc echo*[T](x: varargs[T, `$`]) {.magic: "Echo", tags: [FWriteIO].}
   ## special built-in that takes a variable number of arguments. Each argument
   ## is converted to a string via ``$``, so it works for user-defined
   ## types that have an overloaded ``$`` operator.
@@ -1677,7 +1677,8 @@ proc echo*[T](x: varargs[T, `$`]) {.magic: "Echo".}
   ## Unlike other IO operations this is guaranteed to be thread-safe as
   ## ``echo`` is very often used for debugging convenience.
 
-proc debugEcho*[T](x: varargs[T, `$`]) {.magic: "Echo", noSideEffect.}
+proc debugEcho*[T](x: varargs[T, `$`]) {.magic: "Echo", noSideEffect, 
+                                         tags: [], raises: [].}
   ## Same as ``echo``, but as a special semantic rule, ``debugEcho`` pretends
   ## to be free of side effects, so that it can be used for debugging routines
   ## marked as ``noSideEffect``.
@@ -1780,14 +1781,14 @@ when not defined(EcmaScript) and not defined(NimrodVM):
       ## to ``stderr``.
 
   proc Open*(f: var TFile, filename: string,
-             mode: TFileMode = fmRead, bufSize: int = -1): Bool
+             mode: TFileMode = fmRead, bufSize: int = -1): Bool {.tags: [].}
     ## Opens a file named `filename` with given `mode`.
     ##
     ## Default mode is readonly. Returns true iff the file could be opened.
     ## This throws no exception if the file could not be opened.
 
   proc Open*(f: var TFile, filehandle: TFileHandle,
-             mode: TFileMode = fmRead): Bool
+             mode: TFileMode = fmRead): Bool {.tags: [].}
     ## Creates a ``TFile`` from a `filehandle` with given `mode`.
     ##
     ## Default mode is readonly. Returns true iff the file could be opened.
@@ -1801,56 +1802,57 @@ when not defined(EcmaScript) and not defined(NimrodVM):
     if not open(result, filename, mode, bufSize):
       raise newException(EIO, "cannot open: " & filename)
 
-  proc reopen*(f: TFile, filename: string, mode: TFileMode = fmRead): bool
+  proc reopen*(f: TFile, filename: string, mode: TFileMode = fmRead): bool {.
+    tags: [].}
     ## reopens the file `f` with given `filename` and `mode`. This 
     ## is often used to redirect the `stdin`, `stdout` or `stderr`
     ## file variables.
     ##
     ## Default mode is readonly. Returns true iff the file could be reopened.
 
-  proc Close*(f: TFile) {.importc: "fclose", nodecl.}
+  proc Close*(f: TFile) {.importc: "fclose", nodecl, tags: [].}
     ## Closes the file.
 
-  proc EndOfFile*(f: TFile): Bool
+  proc EndOfFile*(f: TFile): Bool {.tags: [].}
     ## Returns true iff `f` is at the end.
     
-  proc readChar*(f: TFile): char {.importc: "fgetc", nodecl.}
+  proc readChar*(f: TFile): char {.importc: "fgetc", nodecl, tags: [FReadIO].}
     ## Reads a single character from the stream `f`. If the stream
     ## has no more characters, `EEndOfFile` is raised.
-  proc FlushFile*(f: TFile) {.importc: "fflush", noDecl.}
+  proc FlushFile*(f: TFile) {.importc: "fflush", noDecl, tags: [FWriteIO].}
     ## Flushes `f`'s buffer.
 
-  proc readAll*(file: TFile): TaintedString
+  proc readAll*(file: TFile): TaintedString {.tags: [FReadIO].}
     ## Reads all data from the stream `file`. Raises an IO exception
     ## in case of an error
   
-  proc readFile*(filename: string): TaintedString
+  proc readFile*(filename: string): TaintedString {.tags: [FReadIO].}
     ## Opens a file named `filename` for reading. Then calls `readAll`
     ## and closes the file afterwards. Returns the string. 
     ## Raises an IO exception in case of an error.
 
-  proc writeFile*(filename, content: string)
+  proc writeFile*(filename, content: string) {.tags: [FWriteIO].}
     ## Opens a file named `filename` for writing. Then writes the
     ## `content` completely to the file and closes the file afterwards.
     ## Raises an IO exception in case of an error.
 
-  proc write*(f: TFile, r: float)
-  proc write*(f: TFile, i: int)
-  proc write*(f: TFile, i: biggestInt)
-  proc write*(f: TFile, r: biggestFloat)
-  proc write*(f: TFile, s: string)
-  proc write*(f: TFile, b: Bool)
-  proc write*(f: TFile, c: char)
-  proc write*(f: TFile, c: cstring)
-  proc write*(f: TFile, a: varargs[string, `$`])
+  proc write*(f: TFile, r: float) {.tags: [FWriteIO].}
+  proc write*(f: TFile, i: int) {.tags: [FWriteIO].}
+  proc write*(f: TFile, i: biggestInt) {.tags: [FWriteIO].}
+  proc write*(f: TFile, r: biggestFloat) {.tags: [FWriteIO].}
+  proc write*(f: TFile, s: string) {.tags: [FWriteIO].}
+  proc write*(f: TFile, b: Bool) {.tags: [FWriteIO].}
+  proc write*(f: TFile, c: char) {.tags: [FWriteIO].}
+  proc write*(f: TFile, c: cstring) {.tags: [FWriteIO].}
+  proc write*(f: TFile, a: varargs[string, `$`]) {.tags: [FWriteIO].}
     ## Writes a value to the file `f`. May throw an IO exception.
 
-  proc readLine*(f: TFile): TaintedString
+  proc readLine*(f: TFile): TaintedString  {.tags: [FReadIO].}
     ## reads a line of text from the file `f`. May throw an IO exception.
     ## A line of text may be delimited by ``CR``, ``LF`` or
     ## ``CRLF``. The newline character(s) are not part of the returned string.
   
-  proc readLine*(f: TFile, line: var TaintedString): bool
+  proc readLine*(f: TFile, line: var TaintedString): bool {.tags: [FReadIO].}
     ## reads a line of text from the file `f` into `line`. `line` must not be
     ## ``nil``! May throw an IO exception.
     ## A line of text may be delimited by ``CR``, ``LF`` or
@@ -1858,39 +1860,44 @@ when not defined(EcmaScript) and not defined(NimrodVM):
     ## Returns ``false`` if the end of the file has been reached, ``true``
     ## otherwise. If ``false`` is returned `line` contains no new data.
 
-  proc writeln*[Ty](f: TFile, x: varargs[Ty, `$`]) {.inline.}
+  proc writeln*[Ty](f: TFile, x: varargs[Ty, `$`]) {.inline, tags: [FWriteIO].}
     ## writes the values `x` to `f` and then writes "\n".
     ## May throw an IO exception.
 
-  proc getFileSize*(f: TFile): int64
+  proc getFileSize*(f: TFile): int64 {.tags: [FReadIO].}
     ## retrieves the file size (in bytes) of `f`.
 
-  proc ReadBytes*(f: TFile, a: var openarray[int8], start, len: int): int
+  proc ReadBytes*(f: TFile, a: var openarray[int8], start, len: int): int {.
+    tags: [FReadIO].}
     ## reads `len` bytes into the buffer `a` starting at ``a[start]``. Returns
     ## the actual number of bytes that have been read which may be less than
     ## `len` (if not as many bytes are remaining), but not greater.
 
-  proc ReadChars*(f: TFile, a: var openarray[char], start, len: int): int
+  proc ReadChars*(f: TFile, a: var openarray[char], start, len: int): int {.
+    tags: [FReadIO].}
     ## reads `len` bytes into the buffer `a` starting at ``a[start]``. Returns
     ## the actual number of bytes that have been read which may be less than
     ## `len` (if not as many bytes are remaining), but not greater.
 
-  proc readBuffer*(f: TFile, buffer: pointer, len: int): int
+  proc readBuffer*(f: TFile, buffer: pointer, len: int): int {.tags: [FReadIO].}
     ## reads `len` bytes into the buffer pointed to by `buffer`. Returns
     ## the actual number of bytes that have been read which may be less than
     ## `len` (if not as many bytes are remaining), but not greater.
 
-  proc writeBytes*(f: TFile, a: openarray[int8], start, len: int): int
+  proc writeBytes*(f: TFile, a: openarray[int8], start, len: int): int {.
+    tags: [FWriteIO].}
     ## writes the bytes of ``a[start..start+len-1]`` to the file `f`. Returns
     ## the number of actual written bytes, which may be less than `len` in case
     ## of an error.
 
-  proc writeChars*(f: tFile, a: openarray[char], start, len: int): int
+  proc writeChars*(f: tFile, a: openarray[char], start, len: int): int {.
+    tags: [FWriteIO].}
     ## writes the bytes of ``a[start..start+len-1]`` to the file `f`. Returns
     ## the number of actual written bytes, which may be less than `len` in case
     ## of an error.
 
-  proc writeBuffer*(f: TFile, buffer: pointer, len: int): int
+  proc writeBuffer*(f: TFile, buffer: pointer, len: int): int {.
+    tags: [FWriteIO].}
     ## writes the bytes of buffer pointed to by the parameter `buffer` to the
     ## file `f`. Returns the number of actual written bytes, which may be less
     ## than `len` in case of an error.
@@ -1971,7 +1978,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
     ## allows you to override the behaviour of your application when CTRL+C
     ## is pressed. Only one such hook is supported.
     
-  proc writeStackTrace*()
+  proc writeStackTrace*() {.tags: [FWriteIO].}
     ## writes the current stack trace to ``stderr``. This is only works
     ## for debug builds.
   when hostOS != "standalone":
@@ -2029,7 +2036,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
   when hasThreadSupport:
     include "system/channels"
 
-  iterator lines*(filename: string): TaintedString =
+  iterator lines*(filename: string): TaintedString {.tags: [FReadIO].} =
     ## Iterate over any line in the file named `filename`.
     ## If the file does not exist `EIO` is raised.
     var f = open(filename)
@@ -2037,7 +2044,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
     while f.readLine(res): yield res
     close(f)
 
-  iterator lines*(f: TFile): TaintedString =
+  iterator lines*(f: TFile): TaintedString {.tags: [FReadIO].} =
     ## Iterate over any line in the file `f`.
     var res = TaintedString(newStringOfCap(80))
     while f.readLine(res): yield TaintedString(res)
@@ -2308,18 +2315,25 @@ proc InstantiationInfo*(index = -1): tuple[filename: string, line: int] {.
   
 proc raiseAssert*(msg: string) {.noinline.} =
   raise newException(EAssertionFailed, msg)
-  
+
+when true:
+  proc hiddenRaiseAssert(msg: string) {.raises: [].} =
+    # trick the compiler to not list ``EAssertionFailed`` when called
+    # by ``assert``.
+    type THide = proc (msg: string) {.noinline, raises: [], noSideEffect.}
+    THide(raiseAssert)(msg)
+
 template assert*(cond: bool, msg = "") =
   ## provides a means to implement `programming by contracts`:idx: in Nimrod.
   ## ``assert`` evaluates expression ``cond`` and if ``cond`` is false, it
   ## raises an ``EAssertionFailure`` exception. However, the compiler may
   ## not generate any code at all for ``assert`` if it is advised to do so.
   ## Use ``assert`` for debugging purposes only.
-  bind InstantiationInfo
+  bind InstantiationInfo, hiddenRaiseAssert
   when compileOption("assertions"):
     {.line.}:
       if not cond:
-        raiseAssert(astToStr(cond) & ' ' & msg)
+        hiddenRaiseAssert(astToStr(cond) & ' ' & msg)
 
 template doAssert*(cond: bool, msg = "") =
   ## same as `assert` but is always turned on and not affected by the
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
index 02a5893d3..9055cca40 100755
--- a/lib/system/sysio.nim
+++ b/lib/system/sysio.nim
@@ -16,23 +16,25 @@
                        # of the standard library!
 
 
-proc fputs(c: cstring, f: TFile) {.importc: "fputs", noDecl.}
-proc fgets(c: cstring, n: int, f: TFile): cstring {.importc: "fgets", noDecl.}
-proc fgetc(stream: TFile): cint {.importc: "fgetc", nodecl.}
-proc ungetc(c: cint, f: TFile) {.importc: "ungetc", nodecl.}
-proc putc(c: Char, stream: TFile) {.importc: "putc", nodecl.}
-proc fprintf(f: TFile, frmt: CString) {.importc: "fprintf", nodecl, varargs.}
-proc strlen(c: cstring): int {.importc: "strlen", nodecl.}
+proc fputs(c: cstring, f: TFile) {.importc: "fputs", noDecl, tags: [FWriteIO].}
+proc fgets(c: cstring, n: int, f: TFile): cstring {.importc: "fgets", noDecl,
+                                                    tags: [FReadIO].}
+proc fgetc(stream: TFile): cint {.importc: "fgetc", nodecl, tags: [FReadIO].}
+proc ungetc(c: cint, f: TFile) {.importc: "ungetc", nodecl, tags: [].}
+proc putc(c: Char, stream: TFile) {.importc: "putc", nodecl, tags: [FWriteIO].}
+proc fprintf(f: TFile, frmt: CString) {.importc: "fprintf", nodecl, varargs,
+                                        tags: [FWriteIO].}
+proc strlen(c: cstring): int {.importc: "strlen", nodecl, tags: [].}
 
 
 # C routine that is used here:
 proc fread(buf: Pointer, size, n: int, f: TFile): int {.
-  importc: "fread", noDecl.}
+  importc: "fread", noDecl, tags: [FReadIO].}
 proc fseek(f: TFile, offset: clong, whence: int): int {.
-  importc: "fseek", noDecl.}
-proc ftell(f: TFile): int {.importc: "ftell", noDecl.}
+  importc: "fseek", noDecl, tags: [].}
+proc ftell(f: TFile): int {.importc: "ftell", noDecl, tags: [].}
 proc setvbuf(stream: TFile, buf: pointer, typ, size: cint): cint {.
-  importc, nodecl.}
+  importc, nodecl, tags: [].}
 
 proc write(f: TFile, c: cstring) = fputs(c, f)
 
diff --git a/todo.txt b/todo.txt
index e17993331..412696681 100755
--- a/todo.txt
+++ b/todo.txt
@@ -1,7 +1,9 @@
 version 0.9.2
 =============
 
+- make the stdlib aware of tags: sockets, os
 - test&finish first class iterators:
+  * allow return in first class iterators
   * nested iterators
   * arglist as a type?
   * tyIterator?
diff --git a/web/question.txt b/web/question.txt
index 46cbb4f88..8e301b6bd 100755
--- a/web/question.txt
+++ b/web/question.txt
@@ -19,13 +19,6 @@ between threads, so no "stop the world" mechanism is necessary. An unsafe
 shared memory heap is also provided for the increased efficiency that results 
 from that model.
 
-..
-  Don't give me that marketing crap. What is Nimrod?
-  --------------------------------------------------
-
-  Nimrod = Mutable value based datatypes + static binding + sugar to make 
-    this programming modell as convenient as possible
-
 
 Why is it named Nimrod?
 -----------------------
@@ -57,10 +50,11 @@ memory heap.
 How is Nimrod licensed?
 -----------------------
 
-The Nimrod compiler is GPL licensed, the runtime library is LGPL licensed.
+The Nimrod compiler is GPL licensed, the runtime library is LGPL licensed
+with a special exception that allows for static linking.
 This means that you can use any license for your own programs developed with
-Nimrod. If I receive enough requests with good arguments, I may change the
-license of Nimrod to the BSD license.
+Nimrod.
+
 
 How stable is Nimrod?
 ---------------------