summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/ast.nim13
-rwxr-xr-xcompiler/commands.nim92
-rwxr-xr-xcompiler/msgs.nim3
-rwxr-xr-xcompiler/options.nim3
-rwxr-xr-xcompiler/pragmas.nim21
-rwxr-xr-xcompiler/semdata.nim4
-rwxr-xr-xcompiler/semexprs.nim2
-rwxr-xr-xcompiler/types.nim143
-rwxr-xr-xdoc/advopt.txt1
-rwxr-xr-xdoc/manual.txt84
-rwxr-xr-xlib/core/macros.nim28
-rwxr-xr-xlib/pure/cgi.nim70
-rwxr-xr-xlib/pure/os.nim42
-rwxr-xr-xlib/pure/parseopt.nim2
-rwxr-xr-xlib/system.nim30
-rwxr-xr-xlib/system/sysio.nim6
-rwxr-xr-xtodo.txt4
-rwxr-xr-xweb/news.txt2
18 files changed, 266 insertions, 284 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 85ec2c9b3..200f32ed2 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -143,6 +143,7 @@ type
     nkWhileStmt,          # a while statement
     nkCaseStmt,           # a case statement
     nkVarSection,         # a var section
+    nkLetSection,         # a let section
     nkConstSection,       # a const section
     nkConstDef,           # a const definition
     nkTypeSection,        # a type section (consists of type definitions)
@@ -178,6 +179,8 @@ type
     nkRefTy,
     nkPtrTy,
     nkVarTy,
+    nkConstTy,            # ``const T``
+    nkMutableTy,          # ``mutable T``
     nkDistinctTy,         # distinct type
     nkProcTy,
     nkEnumTy,
@@ -186,7 +189,7 @@ type
   TNodeKinds* = set[TNodeKind]
 
 type
-  TSymFlag* = enum    # already 29 flags!
+  TSymFlag* = enum    # already 30 flags!
     sfUsed,           # read access of sym (for warnings) or simply used
     sfExported,       # symbol is exported from module
     sfFromGeneric,    # symbol is instantiation of a generic; this is needed 
@@ -212,6 +215,7 @@ type
     sfProcvar,        # proc can be passed to a proc var
     sfDiscriminant,   # field is a discriminant in a record/object
     sfDeprecated,     # symbol is deprecated
+    sfError,          # usage of symbol should trigger a compile-time error
     sfInClosure,      # variable is accessed by a closure
     sfThread,         # proc will run as a thread
     sfCompileTime,    # proc can be evaluated at compile time
@@ -254,7 +258,12 @@ type
     tyPointer, tyOpenArray,
     tyString, tyCString, tyForward,
     tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers
-    tyFloat, tyFloat32, tyFloat64, tyFloat128
+    tyFloat, tyFloat32, tyFloat64, tyFloat128,
+    tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64,
+    tyBigNum, 
+    tyConst, tyMutable, tyVarargs, 
+    tyIter, # unused
+    tyProxy # currently unused
 
   TTypeKinds* = set[TTypeKind]
 
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 9e58b0deb..3a2c0d6cb 100755
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -30,94 +30,8 @@ const
       "Copyright (c) 2004-2011 by Andreas Rumpf\n"
 
 const 
-  Usage = """
-Usage:
-  nimrod command [options] [projectfile] [arguments]
-Command:
-  compile, c                compile project with default code generator (C)
-  doc                       generate the documentation for inputfile
-  i                         start Nimrod in interactive mode (limited)
-Arguments:
-  arguments are passed to the program being run (if --run option is selected)
-Options:
-  -p, --path:PATH           add path to search paths
-  -d, --define:SYMBOL       define a conditional symbol
-  -u, --undef:SYMBOL        undefine a conditional symbol
-  -f, --forceBuild          force rebuilding of all modules
-  --stackTrace:on|off       turn stack tracing on|off
-  --lineTrace:on|off        turn line tracing on|off
-  --threads:on|off          turn support for multi-threading on|off
-  -x, --checks:on|off       turn all runtime checks on|off
-  --objChecks:on|off        turn obj conversion checks on|off
-  --fieldChecks:on|off      turn case variant field checks on|off
-  --rangeChecks:on|off      turn range checks on|off
-  --boundChecks:on|off      turn bound checks on|off
-  --overflowChecks:on|off   turn int over-/underflow checks on|off
-  -a, --assertions:on|off   turn assertions on|off
-  --floatChecks:on|off      turn all floating point (NaN/Inf) checks on|off
-  --nanChecks:on|off        turn NaN checks on|off
-  --infChecks:on|off        turn Inf checks on|off
-  --deadCodeElim:on|off     whole program dead code elimination on|off
-  --opt:none|speed|size     optimize not at all or for speed|size
-  --app:console|gui|lib     generate a console|GUI application|dynamic library
-  -r, --run                 run the compiled program with given arguments
-  --advanced                show advanced command line switches
-  -h, --help                show this help
-"""
-
-  AdvancedUsage = """
-Advanced commands:
-  compileToC, cc            compile project with C code generator
-  compileToCpp, cpp         compile project to C++ code
-  compileToOC, objc         compile project to Objective C code
-  rst2html                  convert a reStructuredText file to HTML
-  rst2tex                   convert a reStructuredText file to TeX
-  run                       run the project (with Tiny C backend; buggy!)
-  pretty                    pretty print the inputfile
-  genDepend                 generate a DOT file containing the
-                            module dependency graph
-  dump                      dump all defined conditionals and search paths
-  check                     checks the project for syntax and semantic
-  idetools                  compiler support for IDEs: possible options:
-    --track:FILE,LINE,COL   track a file/cursor position
-    --suggest               suggest all possible symbols at position
-    --def                   list all possible symbols at position
-    --context               list possible invokation context  
-Advanced options:
-  -o, --out:FILE            set the output filename
-  --stdout                  output to stdout
-  -w, --warnings:on|off     turn all warnings on|off
-  --warning[X]:on|off       turn specific warning X on|off
-  --hints:on|off            turn all hints on|off
-  --hint[X]:on|off          turn specific hint X on|off
-  --lib:PATH                set the system library path
-  --nimcache:PATH           set the path used for generated files
-  -c, --compileOnly         compile only; do not assemble or link
-  --noLinking               compile but do not link
-  --noMain                  do not generate a main procedure
-  --genScript               generate a compile script (in the 'nimcache'
-                            subdirectory named 'compile_$project$scriptext')
-  --os:SYMBOL               set the target operating system (cross-compilation)
-  --cpu:SYMBOL              set the target processor (cross-compilation)
-  --debuginfo               enables debug information
-  --debugger:on|off         turn Embedded Nimrod Debugger on|off
-  -t, --passc:OPTION        pass an option to the C compiler
-  -l, --passl:OPTION        pass an option to the linker
-  --genMapping              generate a mapping file containing
-                            (Nimrod, mangled) identifier pairs
-  --lineDir:on|off          generation of #line directive on|off
-  --threadanalysis:on|off   turn thread analysis on|off
-  --skipCfg                 do not read the general configuration file
-  --skipProjCfg             do not read the project's configuration file
-  --gc:refc|boehm|none      use Nimrod's native GC|Boehm GC|no GC
-  --index:FILE              use FILE to generate a documentation index file
-  --putenv:key=value        set an environment variable
-  --listCmd                 list the commands used to execute external programs
-  --parallelBuild=0|1|...   perform a parallel build
-                            value = number of processors (0 for auto-detect)
-  --verbosity:0|1|2|3       set Nimrod's verbosity level (0 is default)
-  -v, --version             show detailed version information
-"""
+  Usage = slurp"doc/basicopt.txt".replace("//", "")
+  AdvancedUsage = slurp"doc/advopt.txt".replace("//", "")
 
 proc getCommandLineDesc(): string = 
   result = `%`(HelpMessage, [VersionAsString, platform.os[platform.hostOS].name, 
@@ -276,6 +190,7 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
   of "symbolfiles": result = contains(gGlobalOptions, optSymbolFiles)
   of "genscript": result = contains(gGlobalOptions, optGenScript)
   of "threads": result = contains(gGlobalOptions, optThreads)
+  of "taintmode": result = contains(gGlobalOptions, optTaintMode)
   else: InvalidCmdLineOption(passCmd1, switch, info)
   
 proc processPath(path: string): string = 
@@ -399,6 +314,7 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
   of "assertions", "a": ProcessOnOffSwitch({optAssert}, arg, pass, info)
   of "deadcodeelim": ProcessOnOffSwitchG({optDeadCodeElim}, arg, pass, info)
   of "threads": ProcessOnOffSwitchG({optThreads}, arg, pass, info)
+  of "taintmode": ProcessOnOffSwitchG({optTaintMode}, arg, pass, info)
   of "opt":
     expectArg(switch, arg, pass, info)
     case arg.normalize
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 7c0f11181..d34c6b410 100755
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -89,7 +89,7 @@ type
     errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, 
     errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, 
     errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, 
-    errXhasSideEffects, errIteratorExpected,
+    errXhasSideEffects, errIteratorExpected, errWrongSymbolX,
     errUser,
     warnCannotOpenFile, 
     warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, 
@@ -315,6 +315,7 @@ const
     errXisNoMacroOrTemplate: "\'$1\' is no macro or template",
     errXhasSideEffects: "\'$1\' can have side effects", 
     errIteratorExpected: "iterator within for loop context expected",
+    errWrongSymbolX: "usage of \'$1\' is a user-defined error", 
     errUser: "$1", 
     warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
     warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", 
diff --git a/compiler/options.nim b/compiler/options.nim
index 0e4960a7e..1af06953d 100755
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -46,7 +46,8 @@ type                          # please make sure we have under 32 options
     optSuggest,               # ideTools: 'suggest'
     optContext,               # ideTools: 'context'
     optDef,                   # ideTools: 'def'
-    optThreadAnalysis         # thread analysis pass
+    optThreadAnalysis,        # thread analysis pass
+    optTaintMode              # taint mode turned on
 
   TGlobalOptions* = set[TGlobalOption]
   TCommands* = enum           # Nimrod's commands
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 039474266..9bc5adf65 100755
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -23,15 +23,15 @@ const
     wMagic, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader, 
     wCompilerProc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge, 
     wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
-    wNoStackFrame}
+    wNoStackFrame, wError}
   converterPragmas* = procPragmas
   methodPragmas* = procPragmas
   macroPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, 
     wMagic, wNosideEffect, wCompilerProc, wDeprecated, wExtern,
-    wImportcpp, wImportobjc}
+    wImportcpp, wImportobjc, wError}
   iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect, 
     wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
-    wImportcpp, wImportobjc}
+    wImportcpp, wImportobjc, wError}
   stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, 
     wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, wLinedir, 
     wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, wFatal, 
@@ -43,14 +43,14 @@ const
     wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame}
   typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, 
     wPure, wHeader, wCompilerProc, wFinal, wSize, wExtern, wShallow, 
-    wImportcpp, wImportobjc}
+    wImportcpp, wImportobjc, wError}
   fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, 
-    wImportcpp, wImportobjc}
+    wImportcpp, wImportobjc, wError}
   varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, 
     wMagic, wHeader, wDeprecated, wCompilerProc, wDynLib, wExtern,
-    wImportcpp, wImportobjc}
+    wImportcpp, wImportobjc, wError}
   constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
-    wExtern, wImportcpp, wImportobjc}
+    wExtern, wImportcpp, wImportobjc, wError}
   procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideEffect,
                       wThread}
   allRoutinePragmas* = procPragmas + iteratorPragmas + lambdaPragmas
@@ -530,7 +530,12 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
             if sym.typ != nil: incl(sym.typ.flags, tfThread)
           of wHint: Message(it.info, hintUser, expectStrLit(c, it))
           of wWarning: Message(it.info, warnUser, expectStrLit(c, it))
-          of wError: LocalError(it.info, errUser, expectStrLit(c, it))
+          of wError: 
+            if sym != nil:
+              noVal(it)
+              incl(sym.flags, sfError)
+            else:            
+              LocalError(it.info, errUser, expectStrLit(c, it))
           of wFatal: Fatal(it.info, errUser, expectStrLit(c, it))
           of wDefine: processDefine(c, it)
           of wUndef: processUndef(c, it)
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 306638d6c..e9c0567d1 100755
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -178,7 +178,9 @@ proc makeRangeType(c: PContext, first, last: biggestInt,
   
 proc markUsed*(n: PNode, s: PSym) = 
   incl(s.flags, sfUsed)
-  if sfDeprecated in s.flags: Message(n.info, warnDeprecated, s.name.s)
+  if {sfDeprecated, sfError} * s.flags != {}:
+    if sfDeprecated in s.flags: Message(n.info, warnDeprecated, s.name.s)
+    if sfError in s.flags: LocalError(n.info, errWrongSymbolX, s.name.s)
   
 proc illFormedAst*(n: PNode) = 
   GlobalError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 3a8b827a0..da64db9a9 100755
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -103,7 +103,7 @@ proc checkConversionBetweenObjects(info: TLineInfo, castDest, src: PType) =
 proc checkConvertible(info: TLineInfo, castDest, src: PType) = 
   const 
     IntegralTypes = {tyBool, tyEnum, tyChar, tyInt..tyFloat128}
-  if sameType(castDest, src): 
+  if sameType(castDest, src) and castDest.sym == src.sym: 
     # don't annoy conversions that may be needed on another processor:
     if not (castDest.kind in {tyInt..tyFloat128, tyNil}): 
       Message(info, hintConvFromXtoItselfNotNeeded, typeToString(castDest))
diff --git a/compiler/types.nim b/compiler/types.nim
index d8d0caf35..144f4052e 100755
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -50,16 +50,19 @@ proc equalParams*(a, b: PNode): TParamsEquality
 proc isOrdinalType*(t: PType): bool
 proc enumHasHoles*(t: PType): bool
 const 
-  abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal}
-  abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal}
-  abstractRange* = {tyGenericInst, tyRange, tyDistinct, tyOrdinal}
-  abstractVarRange* = {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal}
-  abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal}
-
-  skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst}
+  abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal,
+                   tyConst, tyMutable}
+  abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal, 
+                  tyConst, tyMutable}
+  abstractRange* = {tyGenericInst, tyRange, tyDistinct, tyOrdinal,
+                    tyConst, tyMutable}
+  abstractVarRange* = {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal,
+                       tyConst, tyMutable}
+  abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal, tyConst, tyMutable}
+
+  skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyConst, tyMutable}
 
 proc skipTypes*(t: PType, kinds: TTypeKinds): PType
-proc elemType*(t: PType): PType
 proc containsObject*(t: PType): bool
 proc containsGarbageCollectedRef*(typ: PType): bool
 proc containsHiddenPointer*(typ: PType): bool
@@ -122,10 +125,11 @@ proc getOrdValue(n: PNode): biggestInt =
     result = 0
 
 proc isCompatibleToCString(a: PType): bool = 
-  result = false
   if a.kind == tyArray: 
     if (firstOrd(a.sons[0]) == 0) and
-        (skipTypes(a.sons[0], {tyRange}).kind in {tyInt..tyInt64}) and
+        (skipTypes(a.sons[0], {tyRange, tyConst, 
+                               tyMutable, tyGenericInst}).kind in 
+            {tyInt..tyInt64, tyUInt..tyUInt64}) and
         (a.sons[1].kind == tyChar): 
       result = true
   
@@ -142,7 +146,7 @@ proc getProcHeader(sym: PSym): string =
   add(result, ')')
   if n.sons[0].typ != nil: result.add(": " & typeToString(n.sons[0].typ))
   
-proc elemType(t: PType): PType = 
+proc elemType*(t: PType): PType = 
   assert(t != nil)
   case t.kind
   of tyGenericInst, tyDistinct: result = elemType(lastSon(t))
@@ -153,36 +157,7 @@ proc elemType(t: PType): PType =
 proc skipGeneric(t: PType): PType = 
   result = t
   while result.kind == tyGenericInst: result = lastSon(result)
-  
-proc skipRange(t: PType): PType = 
-  result = t
-  while result.kind == tyRange: result = base(result)
-  
-proc skipAbstract(t: PType): PType = 
-  result = t
-  while result.kind in {tyRange, tyGenericInst}: result = lastSon(result)
-  
-proc skipVar(t: PType): PType = 
-  result = t
-  while result.kind == tyVar: result = result.sons[0]
-  
-proc skipVarGeneric(t: PType): PType = 
-  result = t
-  while result.kind in {tyGenericInst, tyVar}: result = lastSon(result)
-  
-proc skipPtrsGeneric(t: PType): PType = 
-  result = t
-  while result.kind in {tyGenericInst, tyVar, tyPtr, tyRef}: 
-    result = lastSon(result)
-  
-proc skipVarGenericRange(t: PType): PType = 
-  result = t
-  while result.kind in {tyGenericInst, tyVar, tyRange}: result = lastSon(result)
-  
-proc skipGenericRange(t: PType): PType = 
-  result = t
-  while result.kind in {tyGenericInst, tyVar, tyRange}: result = lastSon(result)
-  
+      
 proc skipTypes(t: PType, kinds: TTypeKinds): PType = 
   result = t
   while result.kind in kinds: result = lastSon(result)
@@ -190,18 +165,18 @@ proc skipTypes(t: PType, kinds: TTypeKinds): PType =
 proc isOrdinalType(t: PType): bool = 
   assert(t != nil)
   result = (t.Kind in {tyChar, tyInt..tyInt64, tyBool, tyEnum}) or
-      (t.Kind in {tyRange, tyOrdinal}) and isOrdinalType(t.sons[0])
+      (t.Kind in {tyRange, tyOrdinal, tyConst, tyMutable, tyGenericInst}) and
+       isOrdinalType(t.sons[0])
 
 proc enumHasHoles(t: PType): bool = 
   var b = t
-  while b.kind == tyRange: b = b.sons[0]
+  while b.kind in {tyConst, tyMutable, tyRange, tyGenericInst}: b = b.sons[0]
   result = b.Kind == tyEnum and tfEnumHasHoles in b.flags
 
 proc iterOverTypeAux(marker: var TIntSet, t: PType, iter: TTypeIter, 
                      closure: PObject): bool
 proc iterOverNode(marker: var TIntSet, n: PNode, iter: TTypeIter, 
                   closure: PObject): bool = 
-  result = false
   if n != nil: 
     case n.kind
     of nkNone..nkNilLit: 
@@ -289,9 +264,9 @@ proc containsObject(t: PType): bool =
   result = searchTypeFor(t, isObjectPredicate)
 
 proc isObjectWithTypeFieldPredicate(t: PType): bool = 
-  result = (t.kind == tyObject) and (t.sons[0] == nil) and
-      not ((t.sym != nil) and (sfPure in t.sym.flags)) and
-      not (tfFinal in t.flags)
+  result = t.kind == tyObject and t.sons[0] == nil and
+      not (t.sym != nil and sfPure in t.sym.flags) and
+      tfFinal notin t.flags
 
 proc analyseObjectWithTypeFieldAux(t: PType, 
                                    marker: var TIntSet): TTypeFieldResult = 
@@ -310,7 +285,7 @@ proc analyseObjectWithTypeFieldAux(t: PType,
       if res == frHeader: result = frHeader
     if result == frNone: 
       if isObjectWithTypeFieldPredicate(t): result = frHeader
-  of tyGenericInst, tyDistinct: 
+  of tyGenericInst, tyDistinct, tyConst, tyMutable: 
     result = analyseObjectWithTypeFieldAux(lastSon(t), marker)
   of tyArray, tyArrayConstr, tyTuple: 
     for i in countup(0, sonsLen(t) - 1): 
@@ -406,8 +381,6 @@ proc mutateTypeAux(marker: var TIntSet, t: PType, iter: TTypeMutator,
   if not ContainsOrIncl(marker, t.id): 
     for i in countup(0, sonsLen(t) - 1): 
       result.sons[i] = mutateTypeAux(marker, result.sons[i], iter, closure)
-      if (result.sons[i] == nil) and (result.kind == tyGenericInst): 
-        assert(false)
     if t.n != nil: result.n = mutateNode(marker, t.n, iter, closure)
   assert(result != nil)
 
@@ -427,7 +400,10 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
       "distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple", 
       "set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc", 
       "pointer", "OpenArray[$1]", "string", "CString", "Forward", "int", "int8", 
-      "int16", "int32", "int64", "float", "float32", "float64", "float128"]
+      "int16", "int32", "int64", "float", "float32", "float64", "float128",
+      
+      "uint", "uint8", "uint16", "uint32", "uint64", "bignum", "const ",
+      "!", "varargs[$1]", "iter[$1]", "proxy[$1]"]
   var t = typ
   result = ""
   if t == nil: return 
@@ -474,7 +450,7 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
         add(result, typeToString(t.sons[i]))
         if i < sonsLen(t) - 1: add(result, ", ")
     add(result, ']')
-  of tyPtr, tyRef, tyVar: 
+  of tyPtr, tyRef, tyVar, tyMutable, tyConst: 
     result = typeToStr[t.kind] & typeToString(t.sons[0])
   of tyRange: 
     result = "range " & rangeToStr(t.n)
@@ -495,6 +471,8 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
       addSep(prag)
       add(prag, "thread")
     if len(prag) != 0: add(result, "{." & prag & ".}")
+  of tyVarargs, tyIter, tyProxy:
+    result = typeToStr[t.kind] % typeToString(t.sons[0])
   else: 
     result = typeToStr[t.kind]
 
@@ -528,7 +506,8 @@ proc firstOrd(t: PType): biggestInt =
     else: 
       assert(t.n.sons[0].kind == nkSym)
       result = t.n.sons[0].sym.position
-  of tyGenericInst, tyDistinct: result = firstOrd(lastSon(t))
+  of tyGenericInst, tyDistinct, tyConst, tyMutable:
+    result = firstOrd(lastSon(t))
   else: 
     InternalError("invalid kind for first(" & $t.kind & ')')
     result = 0
@@ -553,7 +532,8 @@ proc lastOrd(t: PType): biggestInt =
   of tyEnum: 
     assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
     result = t.n.sons[sonsLen(t.n) - 1].sym.position
-  of tyGenericInst, tyDistinct: result = lastOrd(lastSon(t))
+  of tyGenericInst, tyDistinct, tyConst, tyMutable: 
+    result = lastOrd(lastSon(t))
   else: 
     InternalError("invalid kind for last(" & $t.kind & ')')
     result = 0
@@ -561,21 +541,21 @@ proc lastOrd(t: PType): biggestInt =
 proc lengthOrd(t: PType): biggestInt = 
   case t.kind
   of tyInt64, tyInt32, tyInt: result = lastOrd(t)
-  of tyDistinct: result = lengthOrd(t.sons[0])
+  of tyDistinct, tyConst, tyMutable: result = lengthOrd(t.sons[0])
   else: result = lastOrd(t) - firstOrd(t) + 1
   
 proc equalParam(a, b: PSym): TParamsEquality = 
   if SameTypeOrNil(a.typ, b.typ): 
-    if (a.ast == b.ast): 
+    if a.ast == b.ast: 
       result = paramsEqual
-    elif (a.ast != nil) and (b.ast != nil): 
+    elif a.ast != nil and b.ast != nil: 
       if ExprStructuralEquivalent(a.ast, b.ast): result = paramsEqual
       else: result = paramsIncompatible
-    elif (a.ast != nil): 
+    elif a.ast != nil: 
       result = paramsEqual
-    elif (b.ast != nil): 
+    elif b.ast != nil: 
       result = paramsIncompatible
-  else: 
+  else:
     result = paramsNotEqual
   
 proc equalParams(a, b: PNode): TParamsEquality = 
@@ -611,11 +591,10 @@ proc SameTypeOrNil(a, b: PType): bool =
   if a == b: 
     result = true
   else: 
-    if (a == nil) or (b == nil): result = false
+    if a == nil or b == nil: result = false
     else: result = SameType(a, b)
   
 proc SameLiteral(x, y: PNode): bool = 
-  result = false
   if x.kind == y.kind: 
     case x.kind
     of nkCharLit..nkInt64Lit: result = x.intVal == y.intVal
@@ -625,7 +604,7 @@ proc SameLiteral(x, y: PNode): bool =
   
 proc SameRanges(a, b: PNode): bool = 
   result = SameLiteral(a.sons[0], b.sons[0]) and
-      SameLiteral(a.sons[1], b.sons[1])
+           SameLiteral(a.sons[1], b.sons[1])
 
 proc sameTuple(a, b: PType, DistinctOf: bool): bool = 
   # two tuples are equivalent iff the names, types and positions are the same;
@@ -637,7 +616,7 @@ proc sameTuple(a, b: PType, DistinctOf: bool): bool =
       if DistinctOf: result = equalOrDistinctOf(a.sons[i], b.sons[i])
       else: result = SameType(a.sons[i], b.sons[i])
       if not result: return 
-    if (a.n != nil) and (b.n != nil): 
+    if a.n != nil and b.n != nil: 
       for i in countup(0, sonsLen(a.n) - 1): 
         # check field names: 
         if a.n.sons[i].kind != nkSym: InternalError(a.n.info, "sameTuple")
@@ -659,13 +638,14 @@ proc SameType(x, y: PType): bool =
     return false
   case a.Kind
   of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString, 
-     tyInt..tyFloat128, tyExpr, tyStmt, tyTypeDesc: 
+     tyInt..tyBigNum, tyExpr, tyStmt, tyTypeDesc: 
     result = true
-  of tyEnum, tyForward, tyObject, tyDistinct: result = (a.id == b.id)
+  of tyEnum, tyForward, tyObject, tyDistinct, tyProxy: result = (a.id == b.id)
   of tyTuple: result = sameTuple(a, b, false)
   of tyGenericInst: result = sameType(lastSon(a), lastSon(b))
   of tyGenericParam, tyGenericInvokation, tyGenericBody, tySequence, tyOrdinal, 
-     tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc: 
+     tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc,
+     tyConst, tyMutable, tyVarargs, tyIter: 
     if sonsLen(a) == sonsLen(b): 
       result = true
       for i in countup(0, sonsLen(a) - 1): 
@@ -694,13 +674,14 @@ proc equalOrDistinctOf(x, y: PType): bool =
       return false
   case a.Kind
   of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString, 
-     tyInt..tyFloat128, tyExpr, tyStmt, tyTypeDesc: 
+     tyInt..tyBigNum, tyExpr, tyStmt, tyTypeDesc: 
     result = true
-  of tyEnum, tyForward, tyObject, tyDistinct: result = (a.id == b.id)
+  of tyEnum, tyForward, tyObject, tyDistinct, tyProxy: result = (a.id == b.id)
   of tyTuple: result = sameTuple(a, b, true)
   of tyGenericInst: result = equalOrDistinctOf(lastSon(a), lastSon(b))
   of tyGenericParam, tyGenericInvokation, tyGenericBody, tySequence, tyOrdinal, 
-     tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc: 
+     tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc,
+     tyConst, tyMutable, tyVarargs, tyIter: 
     if sonsLen(a) == sonsLen(b): 
       result = true
       for i in countup(0, sonsLen(a) - 1): 
@@ -769,7 +750,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
     result = false            #InternalError('shit found');
   of tyEmpty, tyNil:
     result = kind == skConst
-  of tyString, tyBool, tyChar, tyEnum, tyInt..tyFloat128, tyCString, tyPointer: 
+  of tyString, tyBool, tyChar, tyEnum, tyInt..tyBigNum, tyCString, tyPointer: 
     result = true
   of tyOrdinal: 
     result = kind == skParam
@@ -778,7 +759,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
   of tyRange: 
     result = skipTypes(t.sons[0], abstractInst).kind in
         {tyChar, tyEnum, tyInt..tyFloat128}
-  of tyOpenArray: 
+  of tyOpenArray, tyVarargs: 
     result = (kind == skParam) and typeAllowedAux(marker, t.sons[0], skVar)
   of tySequence: 
     result = (kind != skConst) and typeAllowedAux(marker, t.sons[0], skVar) or
@@ -788,7 +769,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
         t.sons[1].kind == tyEmpty
   of tyPtr, tyRef:
     result = typeAllowedAux(marker, t.sons[0], skVar)
-  of tyArrayConstr, tyTuple, tySet: 
+  of tyArrayConstr, tyTuple, tySet, tyConst, tyMutable, tyIter, tyProxy: 
     for i in countup(0, sonsLen(t) - 1): 
       result = typeAllowedAux(marker, t.sons[i], kind)
       if not result: break 
@@ -856,19 +837,19 @@ proc computeSizeAux(typ: PType, a: var biggestInt): biggestInt =
     return 
   typ.size = - 2              # mark as being computed
   case typ.kind
-  of tyInt: 
+  of tyInt, tyUInt: 
     result = IntSize
     a = result
-  of tyInt8, tyBool, tyChar: 
+  of tyInt8, tyUInt8, tyBool, tyChar: 
     result = 1
     a = result
-  of tyInt16: 
+  of tyInt16, tyUInt16: 
     result = 2
     a = result
-  of tyInt32, tyFloat32: 
+  of tyInt32, tyUInt32, tyFloat32: 
     result = 4
     a = result
-  of tyInt64, tyFloat64: 
+  of tyInt64, tyUInt64, tyFloat64: 
     result = 8
     a = result
   of tyFloat: 
@@ -878,7 +859,8 @@ proc computeSizeAux(typ: PType, a: var biggestInt): biggestInt =
     if typ.callConv == ccClosure: result = 2 * ptrSize
     else: result = ptrSize
     a = ptrSize
-  of tyNil, tyCString, tyString, tySequence, tyPtr, tyRef, tyVar, tyOpenArray: 
+  of tyNil, tyCString, tyString, tySequence, tyPtr, tyRef, tyVar, tyOpenArray,
+     tyBigNum: 
     result = ptrSize
     a = result
   of tyArray, tyArrayConstr: 
@@ -930,7 +912,8 @@ proc computeSizeAux(typ: PType, a: var biggestInt): biggestInt =
     if result < 0: return 
     if a < maxAlign: a = maxAlign
     result = align(result, a)
-  of tyGenericInst, tyDistinct, tyGenericBody: 
+  of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst, tyIter,
+     tyProxy: 
     result = computeSizeAux(lastSon(typ), a)
   else: 
     #internalError("computeSizeAux()")
diff --git a/doc/advopt.txt b/doc/advopt.txt
index db8e44a8b..fd569da6d 100755
--- a/doc/advopt.txt
+++ b/doc/advopt.txt
@@ -40,6 +40,7 @@ Advanced options:
                             (Nimrod, mangled) identifier pairs
   --lineDir:on|off          generation of #line directive on|off
   --threadanalysis:on|off   turn thread analysis on|off
+  --taintMode:on|off        turn taint mode on|off
   --skipCfg                 do not read the general configuration file
   --skipProjCfg             do not read the project's configuration file
   --gc:refc|boehm|none      use Nimrod's native GC|Boehm GC|no GC
diff --git a/doc/manual.txt b/doc/manual.txt
index 56c1fa95c..122668dfb 100755
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -435,8 +435,9 @@ have no side-effect can be used in constant expressions too:
 

 The rules for compile-time computability are: 

 

-1. Literals are compile-time computable.

-2. Procedure calls of the form ``p(X)`` are compile-time computable if

+1. Literals are compile-time computable.
+2. Type conversions are compile-time computable.

+3. Procedure calls of the form ``p(X)`` are compile-time computable if

    ``p`` is a proc without side-effects (see the `noSideEffect pragma`_ 

    for details) and if ``X`` is a (possibly empty) list of compile-time 

    computable arguments.

@@ -1207,7 +1208,7 @@ Void type
 ~~~~~~~~~

 

 The `void`:idx: type denotes the absense of any type. Parameters of 

-type ``void`` are treated as non-existent, a result ``void`` type means that

+type ``void`` are treated as non-existent, ``void`` as a return type means that

 the procedure does not return a value:

 

 .. code-block:: nimrod

@@ -1293,7 +1294,49 @@ algorithm (in pseudo-code) determines type equality:
 

 Since types are graphs which can have cycles, the above algorithm needs an

 auxiliary set ``s`` to detect this case.

+
+
+Type equality modulo type distinction

+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

+The following algorithm (in pseudo-code) determines whether two types
+are equal with no respect to ``distinct`` types. For brevity the cycle check
+with an auxiliary set ``s`` is omitted:
+

+.. code-block:: nimrod

+  proc typeEqualsOrDistinct(a, b: PType): bool =

+    if a.kind == b.kind:

+      case a.kind

+      of int, intXX, float, floatXX, char, string, cstring, pointer, 

+          bool, nil, void:

+        # leaf type: kinds identical; nothing more to check

+        result = true

+      of ref, ptr, var, set, seq, openarray:

+        result = typeEqualsOrDistinct(a.baseType, b.baseType)

+      of range:

+        result = typeEqualsOrDistinct(a.baseType, b.baseType) and

+          (a.rangeA == b.rangeA) and (a.rangeB == b.rangeB)

+      of array:

+        result = typeEqualsOrDistinct(a.baseType, b.baseType) and

+                 typeEqualsOrDistinct(a.indexType, b.indexType)

+      of tuple:

+        if a.tupleLen == b.tupleLen:

+          for i in 0..a.tupleLen-1:

+            if not typeEqualsOrDistinct(a[i], b[i]): return false

+          result = true

+      of distinct:
+        result = typeEqualsOrDistinct(a.baseType, b.baseType)
+      of object, enum:

+        result = a == b

+      of proc:

+        result = typeEqualsOrDistinct(a.parameterTuple, b.parameterTuple) and

+                 typeEqualsOrDistinct(a.resultType, b.resultType) and

+                 a.callingConvention == b.callingConvention
+    elif a.kind == distinct:
+      result = typeEqualsOrDistinct(a.baseType, b)

+    elif b.kind == distinct:

+      result = typeEqualsOrDistinct(a, b.baseType)

+      

 

 Subtype relation

 ~~~~~~~~~~~~~~~~

@@ -1323,14 +1366,6 @@ algorithm returns true:
   # XXX range types?

   proc isImplicitlyConvertible(a, b: PType): bool =

     case a.kind

-    of proc:

-      if b.kind == proc:

-        var x = a.parameterTuple

-        var y = b.parameterTuple

-        if x.tupleLen == y.tupleLen:

-          for i in 0.. x.tupleLen-1:

-            if not isSubtype(x[i], y[i]): return false

-          result = isSubType(b.resultType, a.resultType)

     of int8:    result = b.kind in {int16, int32, int64, int}

     of int16:   result = b.kind in {int32, int64, int}

     of int32:   result = b.kind in {int64, int}

@@ -1357,10 +1392,9 @@ algorithm returns true:
 

   proc isExplicitlyConvertible(a, b: PType): bool =

     if isImplicitlyConvertible(a, b): return true

+    if typeEqualsOrDistinct(a, b): return true

     if isIntegralType(a) and isIntegralType(b): return true

     if isSubtype(a, b) or isSubtype(b, a): return true

-    if a.kind == distinct and typeEquals(a.baseType, b): return true

-    if b.kind == distinct and typeEquals(b.baseType, a): return true

     return false

     

 The convertible relation can be relaxed by a user-defined type 

@@ -1379,7 +1413,10 @@ The convertible relation can be relaxed by a user-defined type
   # you can use the explicit form too

   x = chr.toInt

   echo x # => 97

-

+
+The type conversion ``T(a)`` is an L-value if ``a`` is an L-value and 
+``typeEqualsOrDistinct(T, type(a))`` holds.

+
 

 Assignment compatibility

 ~~~~~~~~~~~~~~~~~~~~~~~~

@@ -2852,7 +2889,7 @@ as helpers for macros.
 

 noReturn pragma

 ---------------

-The `noreturn`:idx: pragma is used to mark a proc that it never returns. 

+The `noreturn`:idx: pragma is used to mark a proc that never returns. 

 

 

 Acyclic pragma

@@ -2930,8 +2967,17 @@ only consist of an assembler statement.
 error pragma

 ------------

 The `error`:idx: pragma is used to make the compiler output an error message

-with the given content. Compilation currently aborts after an error, but this

-may be changed in later versions.

+with the given content. Compilation does not necessarily abort after an error
+though. 
+
+The ``error`` pragma can also be used to
+annotate a symbol (like an iterator or proc). The *usage* of the symbol then
+triggers a compile-time error. This is especially useful to rule out that some
+operation is valid due to overloading and type conversions: 
+
+.. code-block:: nimrod
+  ## check that underlying int values are compared and not the pointers:
+  proc `==`(x, y: ptr int): bool {.error.}

 

 

 fatal pragma

@@ -3238,7 +3284,7 @@ Even though Nimrod's `thread`:idx: support and semantics are preliminary,
 they should be quite usable already. To enable thread support 

 the ``--threads:on`` command line switch needs to be used. The ``system``

 module then contains several threading primitives. 

-See the `threads <threads.html>`_ and `inboxes <inboxes.html>`_ modules 

+See the `threads <threads.html>`_ and `channels <channels.html>`_ modules 

 for the thread API.

 

 Nimrod's memory model for threads is quite different than that of other common

@@ -3259,7 +3305,7 @@ violations of the `no heap sharing restriction`:idx:\: This restriction implies
 that it is invalid to construct a data structure that consists of memory 

 allocated from different (thread local) heaps. 

 

-Since the semantic checking of threads requires a whole program analysis, 

+Since the semantic checking of threads requires whole program analysis, 

 it is quite expensive and can be turned off with ``--threadanalysis:off`` to 

 improve compile times.

 

diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index bebcab677..e362922c8 100755
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -36,15 +36,18 @@ type
     nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch, 

     nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt, 

     nnkAsmStmt, nnkPragma, nnkIfStmt, nnkWhenStmt, 

-    nnkForStmt, nnkWhileStmt, nnkCaseStmt, nnkVarSection, 

-    nnkConstSection, nnkConstDef, nnkTypeSection, nnkTypeDef, 

+    nnkForStmt, nnkWhileStmt, nnkCaseStmt, 
+    nnkVarSection, nnkLetSection, nnkConstSection, 
+    nnkConstDef, nnkTypeSection, nnkTypeDef, 

     nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt, 

     nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, 

     nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt, 

     nnkIncludeStmt, nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, 

     nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy, 

     nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen, 

-    nnkRefTy, nnkPtrTy, nnkVarTy, nnkDistinctTy, 

+    nnkRefTy, nnkPtrTy, nnkVarTy, 
+    nnkConstTy, nnkMutableTy,
+    nnkDistinctTy, 

     nnkProcTy, nnkEnumTy, nnkEnumFieldDef, nnkReturnToken

   TNimNodeKinds* = set[TNimrodNodeKind]

   TNimrodTypeKind* = enum

@@ -184,7 +187,7 @@ proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
   ## in a string literal node

   return newStrLitNode(repr(n))

 

-proc prettyPrint*(n: PNimrodNode): string {.compileTime.} =

+proc toLisp*(n: PNimrodNode): string {.compileTime.} =

   ## Convert the AST `n` to a human-readable string

   ##

   ## You can use this as a tool to explore the Nimrod's abstract syntax 

@@ -204,10 +207,10 @@ proc prettyPrint*(n: PNimrodNode): string {.compileTime.} =
   of nnkIdent: add(result, $n.ident)

   of nnkSym, nnkNone: assert false

   else:

-    add(result, prettyPrint(n[0]))

+    add(result, toLisp(n[0]))

     for j in 1..n.len-1:

       add(result, ", ")

-      add(result, prettyPrint(n[j]))

+      add(result, toLisp(n[j]))

 

   add(result, ")")

 

@@ -215,7 +218,7 @@ proc toYaml*(n: PNimrodNode): string {.magic: "AstToYaml".}
   ## Converts the AST `n` to an YAML string

   ##

   ## Provides more detailed, potentially harder to digest information

-  ## than `prettyPrint`

+  ## than `toLisp`

 

 proc parseExpr*(s: string) : expr {.magic: "ParseExprToAst".}

   ## Compiles the passed string to its AST representation

@@ -226,10 +229,13 @@ proc parseStmt*(s: string) : stmt {.magic: "ParseStmtToAst".}
   ## Expects one or more statements

 

 proc getAst*(macroOrTemplate: expr): expr {.magic: "ExpandMacroToAst".}

-  ## Obtains the AST nodes returned from a macro or template invocation

-  ## example:

-  ## macro FooMacro() = 

-  ##   var ast = getAst(BarTemplate())

+  ## Obtains the AST nodes returned from a macro or template invocation.

+  ## Example:
+  ## 
+  ## .. code-block:: nimrod
+  ##

+  ##   macro FooMacro() = 

+  ##     var ast = getAst(BarTemplate())

   

 proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =

   ## checks that `n` is of kind `k`. If this is not the case,

diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim
index 47517ae98..c8536236d 100755
--- a/lib/pure/cgi.nim
+++ b/lib/pure/cgi.nim
@@ -107,7 +107,7 @@ proc cgiError*(msg: string) {.noreturn.} =
   raise e
 
 proc getEncodedData(allowedMethods: set[TRequestMethod]): string =
-  case getenv("REQUEST_METHOD")
+  case getenv("REQUEST_METHOD").string
   of "POST":
     if methodPost notin allowedMethods:
       cgiError("'REQUEST_METHOD' 'POST' is not supported")
@@ -192,131 +192,131 @@ proc validateData*(data: PStringTable, validKeys: openarray[string]) =
 
 proc getContentLength*(): string =
   ## returns contents of the ``CONTENT_LENGTH`` environment variable
-  return getenv("CONTENT_LENGTH")
+  return getenv("CONTENT_LENGTH").string
 
 proc getContentType*(): string =
   ## returns contents of the ``CONTENT_TYPE`` environment variable
-  return getenv("CONTENT_Type")
+  return getenv("CONTENT_Type").string
 
 proc getDocumentRoot*(): string =
   ## returns contents of the ``DOCUMENT_ROOT`` environment variable
-  return getenv("DOCUMENT_ROOT")
+  return getenv("DOCUMENT_ROOT").string
 
 proc getGatewayInterface*(): string =
   ## returns contents of the ``GATEWAY_INTERFACE`` environment variable
-  return getenv("GATEWAY_INTERFACE")
+  return getenv("GATEWAY_INTERFACE").string
 
 proc getHttpAccept*(): string =
   ## returns contents of the ``HTTP_ACCEPT`` environment variable
-  return getenv("HTTP_ACCEPT")
+  return getenv("HTTP_ACCEPT").string
 
 proc getHttpAcceptCharset*(): string =
   ## returns contents of the ``HTTP_ACCEPT_CHARSET`` environment variable
-  return getenv("HTTP_ACCEPT_CHARSET")
+  return getenv("HTTP_ACCEPT_CHARSET").string
 
 proc getHttpAcceptEncoding*(): string =
   ## returns contents of the ``HTTP_ACCEPT_ENCODING`` environment variable
-  return getenv("HTTP_ACCEPT_ENCODING")
+  return getenv("HTTP_ACCEPT_ENCODING").string
 
 proc getHttpAcceptLanguage*(): string =
   ## returns contents of the ``HTTP_ACCEPT_LANGUAGE`` environment variable
-  return getenv("HTTP_ACCEPT_LANGUAGE")
+  return getenv("HTTP_ACCEPT_LANGUAGE").string
 
 proc getHttpConnection*(): string =
   ## returns contents of the ``HTTP_CONNECTION`` environment variable
-  return getenv("HTTP_CONNECTION")
+  return getenv("HTTP_CONNECTION").string
 
 proc getHttpCookie*(): string =
   ## returns contents of the ``HTTP_COOKIE`` environment variable
-  return getenv("HTTP_COOKIE")
+  return getenv("HTTP_COOKIE").string
 
 proc getHttpHost*(): string =
   ## returns contents of the ``HTTP_HOST`` environment variable
-  return getenv("HTTP_HOST")
+  return getenv("HTTP_HOST").string
 
 proc getHttpReferer*(): string =
   ## returns contents of the ``HTTP_REFERER`` environment variable
-  return getenv("HTTP_REFERER")
+  return getenv("HTTP_REFERER").string
 
 proc getHttpUserAgent*(): string =
   ## returns contents of the ``HTTP_USER_AGENT`` environment variable
-  return getenv("HTTP_USER_AGENT")
+  return getenv("HTTP_USER_AGENT").string
 
 proc getPathInfo*(): string =
   ## returns contents of the ``PATH_INFO`` environment variable
-  return getenv("PATH_INFO")
+  return getenv("PATH_INFO").string
 
 proc getPathTranslated*(): string =
   ## returns contents of the ``PATH_TRANSLATED`` environment variable
-  return getenv("PATH_TRANSLATED")
+  return getenv("PATH_TRANSLATED").string
 
 proc getQueryString*(): string =
   ## returns contents of the ``QUERY_STRING`` environment variable
-  return getenv("QUERY_STRING")
+  return getenv("QUERY_STRING").string
 
 proc getRemoteAddr*(): string =
   ## returns contents of the ``REMOTE_ADDR`` environment variable
-  return getenv("REMOTE_ADDR")
+  return getenv("REMOTE_ADDR").string
 
 proc getRemoteHost*(): string =
   ## returns contents of the ``REMOTE_HOST`` environment variable
-  return getenv("REMOTE_HOST")
+  return getenv("REMOTE_HOST").string
 
 proc getRemoteIdent*(): string =
   ## returns contents of the ``REMOTE_IDENT`` environment variable
-  return getenv("REMOTE_IDENT")
+  return getenv("REMOTE_IDENT").string
 
 proc getRemotePort*(): string =
   ## returns contents of the ``REMOTE_PORT`` environment variable
-  return getenv("REMOTE_PORT")
+  return getenv("REMOTE_PORT").string
 
 proc getRemoteUser*(): string =
   ## returns contents of the ``REMOTE_USER`` environment variable
-  return getenv("REMOTE_USER")
+  return getenv("REMOTE_USER").string
 
 proc getRequestMethod*(): string =
   ## returns contents of the ``REQUEST_METHOD`` environment variable
-  return getenv("REQUEST_METHOD")
+  return getenv("REQUEST_METHOD").string
 
 proc getRequestURI*(): string =
   ## returns contents of the ``REQUEST_URI`` environment variable
-  return getenv("REQUEST_URI")
+  return getenv("REQUEST_URI").string
 
 proc getScriptFilename*(): string =
   ## returns contents of the ``SCRIPT_FILENAME`` environment variable
-  return getenv("SCRIPT_FILENAME")
+  return getenv("SCRIPT_FILENAME").string
 
 proc getScriptName*(): string =
   ## returns contents of the ``SCRIPT_NAME`` environment variable
-  return getenv("SCRIPT_NAME")
+  return getenv("SCRIPT_NAME").string
 
 proc getServerAddr*(): string =
   ## returns contents of the ``SERVER_ADDR`` environment variable
-  return getenv("SERVER_ADDR")
+  return getenv("SERVER_ADDR").string
 
 proc getServerAdmin*(): string =
   ## returns contents of the ``SERVER_ADMIN`` environment variable
-  return getenv("SERVER_ADMIN")
+  return getenv("SERVER_ADMIN").string
 
 proc getServerName*(): string =
   ## returns contents of the ``SERVER_NAME`` environment variable
-  return getenv("SERVER_NAME")
+  return getenv("SERVER_NAME").string
 
 proc getServerPort*(): string =
   ## returns contents of the ``SERVER_PORT`` environment variable
-  return getenv("SERVER_PORT")
+  return getenv("SERVER_PORT").string
 
 proc getServerProtocol*(): string =
   ## returns contents of the ``SERVER_PROTOCOL`` environment variable
-  return getenv("SERVER_PROTOCOL")
+  return getenv("SERVER_PROTOCOL").string
 
 proc getServerSignature*(): string =
   ## returns contents of the ``SERVER_SIGNATURE`` environment variable
-  return getenv("SERVER_SIGNATURE")
+  return getenv("SERVER_SIGNATURE").string
 
 proc getServerSoftware*(): string =
   ## returns contents of the ``SERVER_SOFTWARE`` environment variable
-  return getenv("SERVER_SOFTWARE")
+  return getenv("SERVER_SOFTWARE").string
 
 proc setTestData*(keysvalues: openarray[string]) =
   ## fills the appropriate environment variables to test your CGI application.
@@ -360,10 +360,10 @@ proc setCookie*(name, value: string) =
 var
   gcookies: PStringTable = nil
 
-proc getCookie*(name: string): string =
+proc getCookie*(name: string): TaintedString =
   ## Gets a cookie. If no cookie of `name` exists, "" is returned.
   if gcookies == nil: gcookies = parseCookies(getHttpCookie())
-  result = gcookies[name]
+  result = TaintedString(gcookies[name])
 
 proc existsCookie*(name: string): bool =
   ## Checks if a cookie of `name` exists.
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index ccbf98828..50c6ffeba 100755
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -692,7 +692,7 @@ proc findEnvVar(key: string): int =
     if startsWith(environment[i], temp): return i
   return -1
 
-proc getEnv*(key: string): string =
+proc getEnv*(key: string): TaintedString =
   ## Returns the value of the `environment variable`:idx: named `key`.
   ##
   ## If the variable does not exist, "" is returned. To distinguish
@@ -700,11 +700,11 @@ proc getEnv*(key: string): string =
   ## `existsEnv(key)`.
   var i = findEnvVar(key)
   if i >= 0:
-    return substr(environment[i], find(environment[i], '=')+1)
+    return TaintedString(substr(environment[i], find(environment[i], '=')+1))
   else:
     var env = cgetenv(key)
-    if env == nil: return ""
-    result = $env
+    if env == nil: return TaintedString("")
+    result = TaintedString($env)
 
 proc existsEnv*(key: string): bool =
   ## Checks whether the environment variable named `key` exists.
@@ -733,14 +733,15 @@ proc putEnv*(key, val: string) =
     if SetEnvironmentVariableA(key, val) == 0'i32:
       OSError()
 
-iterator envPairs*(): tuple[key, value: string] =
+iterator envPairs*(): tuple[key, value: TaintedString] =
   ## Iterate over all `environments variables`:idx:. In the first component 
   ## of the tuple is the name of the current variable stored, in the second
   ## its value.
   getEnvVarsC()
   for i in 0..high(environment):
     var p = find(environment[i], '=')
-    yield (substr(environment[i], 0, p-1), substr(environment[i], p+1))
+    yield (TaintedString(substr(environment[i], 0, p-1)), 
+           TaintedString(substr(environment[i], p+1)))
 
 iterator walkFiles*(pattern: string): string =
   ## Iterate over all the files that match the `pattern`. On POSIX this uses
@@ -1078,18 +1079,18 @@ proc exclFilePermissions*(filename: string,
 
 proc getHomeDir*(): string {.rtl, extern: "nos$1".} =
   ## Returns the home directory of the current user.
-  when defined(windows): return getEnv("USERPROFILE") & "\\"
-  else: return getEnv("HOME") & "/"
+  when defined(windows): return string(getEnv("USERPROFILE")) & "\\"
+  else: return string(getEnv("HOME")) & "/"
 
 proc getConfigDir*(): string {.rtl, extern: "nos$1".} =
   ## Returns the config directory of the current user for applications.
-  when defined(windows): return getEnv("APPDATA") & "\\"
-  else: return getEnv("HOME") & "/.config/"
+  when defined(windows): return string(getEnv("APPDATA")) & "\\"
+  else: return string(getEnv("HOME")) & "/.config/"
 
 proc getTempDir*(): string {.rtl, extern: "nos$1".} =
   ## Returns the temporary directory of the current user for applications to
   ## save temporary files in.
-  when defined(windows): return getEnv("TEMP") & "\\"
+  when defined(windows): return string(getEnv("TEMP")) & "\\"
   else: return "/tmp/"
 
 when defined(windows):
@@ -1107,14 +1108,14 @@ when defined(windows):
     if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
     result = ownArgv.len-1
 
-  proc paramStr*(i: int): string {.rtl, extern: "nos$1".} =
+  proc paramStr*(i: int): TaintedString {.rtl, extern: "nos$1".} =
     ## Returns the `i`-th `command line argument`:idx: given to the
     ## application.
     ##
     ## `i` should be in the range `1..paramCount()`, else
     ## the `EOutOfIndex` exception is raised.
     if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
-    return ownArgv[i]
+    return TaintedString(ownArgv[i])
 
 elif not defined(createNimRtl):
   # On Posix, there is no portable way to get the command line from a DLL.
@@ -1122,8 +1123,8 @@ elif not defined(createNimRtl):
     cmdCount {.importc: "cmdCount".}: cint
     cmdLine {.importc: "cmdLine".}: cstringArray
 
-  proc paramStr*(i: int): string =
-    if i < cmdCount and i >= 0: return $cmdLine[i]
+  proc paramStr*(i: int): TaintedString =
+    if i < cmdCount and i >= 0: return TaintedString($cmdLine[i])
     raise newException(EInvalidIndex, "invalid index")
 
   proc paramCount*(): int = return cmdCount-1
@@ -1172,13 +1173,14 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1".} =
       result = "" # error!
   else:
     # little heuristic that may work on other POSIX-like systems:
-    result = getEnv("_")
+    result = string(getEnv("_"))
     if len(result) == 0:
-      result = ParamStr(0) # POSIX guaranties that this contains the executable
-                           # as it has been executed by the calling process
+      result = string(ParamStr(0))
+      # POSIX guaranties that this contains the executable
+      # as it has been executed by the calling process
       if len(result) > 0 and result[0] != DirSep: # not an absolute path?
         # iterate over any path in the $PATH environment variable
-        for p in split(getEnv("PATH"), {PathSep}):
+        for p in split(string(getEnv("PATH")), {PathSep}):
           var x = joinPath(p, result)
           if ExistsFile(x): return x
 
@@ -1230,7 +1232,7 @@ proc findExe*(exe: string): string =
   ## is added an ``.exe`` file extension if it has no extension.
   result = addFileExt(exe, os.exeExt)
   if ExistsFile(result): return
-  var path = os.getEnv("PATH")
+  var path = string(os.getEnv("PATH"))
   for candidate in split(path, pathSep): 
     var x = candidate / result
     if ExistsFile(x): return x
diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim
index 1981c9242..347871bac 100755
--- a/lib/pure/parseopt.nim
+++ b/lib/pure/parseopt.nim
@@ -48,7 +48,7 @@ when defined(os.ParamCount):
     else: 
       result.cmd = ""
       for i in countup(1, ParamCount()): 
-        result.cmd = result.cmd & quoteIfContainsWhite(paramStr(i)) & ' '
+        result.cmd = result.cmd & quoteIfContainsWhite(paramStr(i).string) & ' '
     result.kind = cmdEnd
     result.key = ""
     result.val = ""
diff --git a/lib/system.nim b/lib/system.nim
index ea004b925..8e4205189 100755
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -787,6 +787,19 @@ proc compileOption*(option, arg: string): bool {.
 const
   hasThreadSupport = compileOption("threads")
   hasSharedHeap = defined(boehmgc) # don't share heaps; every thread has its own
+#  taintMode = compileOption("taintmode")
+
+when defined(taintMode):
+  # XXX use a compile time option for it!
+  type TaintedString* = distinct string ## a distinct string type that 
+                                        ## is `tainted`:idx:. It is an alias for
+                                        ## ``string`` if the taint mode is not
+                                        ## turned on. Use the ``-d:taintMode``
+                                        ## command line switch to turn the taint
+                                        ## mode on.
+else:
+  type TaintedString* = string
+
 
 when hasThreadSupport:
   {.pragma: rtlThreadVar, threadvar.}
@@ -896,11 +909,6 @@ type # these work for most platforms:
   PInt64* = ptr Int64 ## an alias for ``ptr int64``
   PInt32* = ptr Int32 ## an alias for ``ptr int32``
 
-type TOptional*[T] = object
-  case hasValue* : bool
-  of true: value*: T
-  of false: nil
- 
 proc toFloat*(i: int): float {.
   magic: "ToFloat", noSideEffect, importc: "toFloat".}
   ## converts an integer `i` into a ``float``. If the conversion
@@ -1653,7 +1661,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
   proc FlushFile*(f: TFile) {.importc: "fflush", noDecl.}
     ## Flushes `f`'s buffer.
 
-  proc readFile*(filename: string): string
+  proc readFile*(filename: string): TaintedString
     ## Opens a file named `filename` for reading. Then reads the
     ## file's content completely into a string and
     ## closes the file afterwards. Returns the string. 
@@ -1675,7 +1683,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
   proc write*(f: TFile, a: openArray[string])
     ## Writes a value to the file `f`. May throw an IO exception.
 
-  proc readLine*(f: TFile): string
+  proc readLine*(f: TFile): TaintedString
     ## 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.
@@ -1814,22 +1822,22 @@ when not defined(EcmaScript) and not defined(NimrodVM):
   when hasThreadSupport:
     include "system/channels"
 
-  iterator lines*(filename: string): string =
+  iterator lines*(filename: string): TaintedString =
     ## Iterate over any line in the file named `filename`.
     ## If the file does not exist `EIO` is raised.
     var f = open(filename)
     var res = ""
     while not endOfFile(f):
       rawReadLine(f, res)
-      yield res
+      yield TaintedString(res)
     Close(f)
 
-  iterator lines*(f: TFile): string =
+  iterator lines*(f: TFile): TaintedString =
     ## Iterate over any line in the file `f`.
     var res = ""
     while not endOfFile(f):
       rawReadLine(f, res)
-      yield res
+      yield TaintedString(res)
 
   include "system/assign"
   include "system/repr"
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
index d48e87ae2..56815c0c5 100755
--- a/lib/system/sysio.nim
+++ b/lib/system/sysio.nim
@@ -55,8 +55,8 @@ proc rawReadLine(f: TFile, result: var string) =
       break
     add result, chr(int(c))
 
-proc readLine(f: TFile): string =
-  result = ""
+proc readLine(f: TFile): TaintedString =
+  result = TaintedString("")
   rawReadLine(f, result)
 
 proc write(f: TFile, i: int) = 
@@ -81,7 +81,7 @@ proc write(f: TFile, c: Char) = putc(c, f)
 proc write(f: TFile, a: openArray[string]) =
   for x in items(a): write(f, x)
 
-proc readFile(filename: string): string =
+proc readFile(filename: string): TaintedString =
   var f = open(filename)
   try:
     var len = getFileSize(f)
diff --git a/todo.txt b/todo.txt
index 6fff06b95..7a2c6dbf7 100755
--- a/todo.txt
+++ b/todo.txt
@@ -2,14 +2,14 @@ Version 0.8.14
 ==============
 
 - 'let x = y'
-- threads should not have an inbox per default
+- T(x) as l-value
+- fix actors.nim
 - make threadvar efficient again on linux after testing
 - fix the 'const' issues
 - test the sort implementation again
 - optional indentation for 'case' statement
 - taint mode
 - const ptr/ref
-- {.error.} pragma for proc headers
 
 
 version 0.9.0
diff --git a/web/news.txt b/web/news.txt
index bd2d970d2..462133d24 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -45,6 +45,8 @@ Language Additions
 - The built-in type ``void`` can be used to denote the absense of any type.
   This is useful in generic code.
 - Return types may be of the type ``var T`` to return an l-value.
+- The error pragma can now be used to mark symbols whose *usage* should trigger
+  a compile-time error.
 
 
 Compiler Additions