summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/ast.nim7
-rw-r--r--compiler/ccgcalls.nim2
-rwxr-xr-xcompiler/ccgexprs.nim4
-rwxr-xr-xcompiler/ccgstmts.nim2
-rwxr-xr-xcompiler/ccgtypes.nim2
-rwxr-xr-xcompiler/cgen.nim3
-rwxr-xr-xcompiler/docgen.nim18
-rwxr-xr-xcompiler/ecmasgen.nim6
-rwxr-xr-xcompiler/evals.nim4
-rwxr-xr-xcompiler/msgs.nim3
-rwxr-xr-xcompiler/nversion.nim2
-rwxr-xr-xcompiler/parser.nim2
-rwxr-xr-xcompiler/pragmas.nim1
-rwxr-xr-xcompiler/renderer.nim27
-rwxr-xr-xcompiler/rodwrite.nim11
-rwxr-xr-xcompiler/semexprs.nim3
-rwxr-xr-xcompiler/semgnrc.nim2
-rwxr-xr-xcompiler/semstmts.nim25
-rwxr-xr-xcompiler/semthreads.nim4
-rwxr-xr-xcompiler/semtypes.nim3
-rwxr-xr-xcompiler/transf.nim6
-rwxr-xr-xcompiler/types.nim2
-rwxr-xr-xdoc/manual.txt14
-rwxr-xr-xdoc/tut1.txt25
-rwxr-xr-xdoc/tut2.txt10
-rwxr-xr-xlib/core/macros.nim4
-rwxr-xr-xlib/system/threads.nim3
-rw-r--r--tests/reject/tlet.nim11
-rw-r--r--tests/reject/tlet2.nim16
-rw-r--r--tests/run/tlet.nim19
-rwxr-xr-xtests/tester.nim18
-rwxr-xr-xtodo.txt11
-rwxr-xr-xweb/news.txt2
33 files changed, 169 insertions, 103 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index d80f2289e..3d07fda07 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -144,11 +144,11 @@ type
     nkForStmt,            # a for statement
     nkWhileStmt,          # a while statement
     nkCaseStmt,           # a case statement
+    nkTypeSection,        # a type section (consists of type definitions)
     nkVarSection,         # a var section
     nkLetSection,         # a let section
     nkConstSection,       # a const section
     nkConstDef,           # a const definition
-    nkTypeSection,        # a type section (consists of type definitions)
     nkTypeDef,            # a type definition
     nkYieldStmt,          # the yield statement as a tree
     nkTryStmt,            # a try statement
@@ -315,8 +315,9 @@ type
     skTemp,               # a temporary variable (introduced by compiler)
     skModule,             # module identifier
     skType,               # a type
-    skConst,              # a constant
     skVar,                # a variable
+    skLet,                # a 'let' symbol
+    skConst,              # a constant
     skResult,             # special 'result' variable
     skProc,               # a proc
     skMethod,             # a method
@@ -580,7 +581,7 @@ const
   ConstantDataTypes*: TTypeKinds = {tyArrayConstr, tyArray, tySet, 
                                     tyTuple, tySequence}
   ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator, 
-    skMacro, skTemplate, skConverter, skEnumField, skStub}
+    skMacro, skTemplate, skConverter, skEnumField, skLet, skStub}
   PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfAllConst}
   namePos* = 0
   genericParamsPos* = 1
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index dbb9190d2..1c57479ae 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -53,7 +53,7 @@ proc isInCurrentFrame(p: BProc, n: PNode): bool =
   # this does not work reliably because of forwarding + inlining can break it
   case n.kind
   of nkSym:
-    if n.sym.kind in {skVar, skResult, skTemp} and p.prc != nil:
+    if n.sym.kind in {skVar, skResult, skTemp, skLet} and p.prc != nil:
       result = p.prc.id == n.sym.owner.id
   of nkDotExpr, nkBracketExpr:
     if skipTypes(n.sons[0].typ, abstractInst).kind notin {tyVar,tyPtr,tyRef}:
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 4138aecd2..caaab1ab5 100755
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -139,7 +139,7 @@ proc getStorageLoc(n: PNode): TStorageLoc =
     case n.sym.kind
     of skParam, skForVar, skTemp:
       result = OnStack
-    of skVar, skResult:
+    of skVar, skResult, skLet:
       if sfGlobal in n.sym.flags: result = OnHeap
       else: result = OnStack
     of skConst: 
@@ -1607,7 +1607,7 @@ proc expr(p: BProc, e: PNode, d: var TLoc) =
         genComplexConst(p, sym, d)
     of skEnumField:
       putIntoDest(p, d, e.typ, toRope(sym.position))
-    of skVar, skResult:
+    of skVar, skResult, skLet:
       if sfGlobal in sym.flags: genVarPrototype(p.module, sym)
       if sym.loc.r == nil or sym.loc.t == nil:
         InternalError(e.info, "expr: var not init " & sym.name.s)
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index ca6f1fd91..da9a05188 100755
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -666,7 +666,7 @@ proc genStmts(p: BProc, t: PNode) =
   of nkBlockStmt: genBlock(p, t, a)
   of nkIfStmt: genIfStmt(p, t)
   of nkWhileStmt: genWhileStmt(p, t)
-  of nkVarSection: genVarStmt(p, t)
+  of nkVarSection, nkLetSection: genVarStmt(p, t)
   of nkConstSection: genConstStmt(p, t)
   of nkForStmt: internalError(t.info, "for statement not eliminated")
   of nkCaseStmt: genCaseStmt(p, t)
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index bfd8c8723..490282fae 100755
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -37,7 +37,7 @@ proc mangleName(s: PSym): PRope =
       case s.kind
       of skProc, skMethod, skConverter, skConst: 
         result = toRope("@")
-      of skVar, skResult: 
+      of skVar, skResult, skLet: 
         if sfGlobal in s.flags: result = toRope("@")
         else: result = toRope("%")
       of skForVar, skTemp, skParam, skType, skEnumField, skModule: 
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index ca58e4b1c..5c730af80 100755
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -339,6 +339,7 @@ proc assignLocalVar(p: BProc, s: PSym) =
   # for each module that uses them!
   if s.loc.k == locNone: 
     fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack)
+    if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
   app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t))
   if sfRegister in s.flags: app(p.s[cpsLocals], " register")
   if (sfVolatile in s.flags) or (p.nestedTryStmts.len > 0): 
@@ -473,7 +474,7 @@ proc cgsym(m: BModule, name: string): PRope =
   if sym != nil: 
     case sym.kind
     of skProc, skMethod, skConverter: genProc(m, sym)
-    of skVar, skResult: genVarPrototype(m, sym)
+    of skVar, skResult, skLet: genVarPrototype(m, sym)
     of skType: discard getTypeDesc(m, sym.typ)
     else: InternalError("cgsym: " & name)
   else:
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index e76545c22..5e1e4b59c 100755
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -785,18 +785,12 @@ proc generateDoc(d: PDoc, n: PNode) =
   of nkMacroDef: genItem(d, n, n.sons[namePos], skMacro)
   of nkTemplateDef: genItem(d, n, n.sons[namePos], skTemplate)
   of nkConverterDef: genItem(d, n, n.sons[namePos], skConverter)
-  of nkVarSection: 
-    for i in countup(0, sonsLen(n) - 1): 
+  of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
+    for i in countup(0, sonsLen(n) - 1):
       if n.sons[i].kind != nkCommentStmt: 
-        genItem(d, n.sons[i], n.sons[i].sons[0], skVar)
-  of nkConstSection: 
-    for i in countup(0, sonsLen(n) - 1): 
-      if n.sons[i].kind != nkCommentStmt: 
-        genItem(d, n.sons[i], n.sons[i].sons[0], skConst)
-  of nkTypeSection: 
-    for i in countup(0, sonsLen(n) - 1): 
-      if n.sons[i].kind != nkCommentStmt: 
-        genItem(d, n.sons[i], n.sons[i].sons[0], skType)
+        # order is always 'type var let const':
+        genItem(d, n.sons[i], n.sons[i].sons[0], 
+                succ(skType, ord(n.kind)-ord(nkTypeSection)))
   of nkStmtList: 
     for i in countup(0, sonsLen(n) - 1): generateDoc(d, n.sons[i])
   of nkWhenStmt: 
@@ -810,7 +804,7 @@ proc generateDoc(d: PDoc, n: PNode) =
 
 proc genSection(d: PDoc, kind: TSymKind) = 
   const sectionNames: array[skModule..skTemplate, string] = [
-    "Imports", "Types", "Consts", "Vars", "Vars", "Procs", "Methods", 
+    "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods", 
     "Iterators", "Converters", "Macros", "Templates"
   ]
   if d.section[kind] == nil: return 
diff --git a/compiler/ecmasgen.nim b/compiler/ecmasgen.nim
index 3d9e99d44..588abfc93 100755
--- a/compiler/ecmasgen.nim
+++ b/compiler/ecmasgen.nim
@@ -835,7 +835,7 @@ proc genAddr(p: var TProc, n: PNode, r: var TCompRes) =
     s = n.sons[0].sym
     if s.loc.r == nil: InternalError(n.info, "genAddr: 3")
     case s.kind
-    of skVar, skResult: 
+    of skVar, skLet, skResult: 
       if mapType(n.typ) == etyObject: 
         # make addr() a no-op:
         r.kind = etyNone
@@ -866,7 +866,7 @@ proc genSym(p: var TProc, n: PNode, r: var TCompRes) =
   if s.loc.r == nil: 
     InternalError(n.info, "symbol has no generated name: " & s.name.s)
   case s.kind
-  of skVar, skParam, skTemp, skResult: 
+  of skVar, skLet, skParam, skTemp, skResult: 
     var k = mapType(s.typ)
     if k == etyBaseIndex: 
       r.kind = etyBaseIndex
@@ -1339,7 +1339,7 @@ proc genStmt(p: var TProc, n: PNode, r: var TCompRes) =
   of nkBlockStmt: genBlock(p, n, r)
   of nkIfStmt: genIfStmt(p, n, r)
   of nkWhileStmt: genWhileStmt(p, n, r)
-  of nkVarSection: genVarStmt(p, n, r)
+  of nkVarSection, nkLetSection: genVarStmt(p, n, r)
   of nkConstSection: genConstStmt(p, n, r)
   of nkForStmt: internalError(n.info, "for statement not eliminated")
   of nkCaseStmt: genCaseStmt(p, n, r)
diff --git a/compiler/evals.nim b/compiler/evals.nim
index c92143a5b..c44ea3aeb 100755
--- a/compiler/evals.nim
+++ b/compiler/evals.nim
@@ -452,7 +452,7 @@ proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
   case s.kind
   of skProc, skConverter, skMacro: 
     result = s.getBody
-  of skVar, skForVar, skTemp, skResult:
+  of skVar, skLet, skForVar, skTemp, skResult:
     if sfGlobal notin s.flags:
       result = evalVariable(c.tos, s, flags)
     else:
@@ -1195,7 +1195,7 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
   of nkWhenStmt, nkIfStmt, nkIfExpr: result = evalIf(c, n)
   of nkWhileStmt: result = evalWhile(c, n)
   of nkCaseStmt: result = evalCase(c, n)
-  of nkVarSection: result = evalVar(c, n)
+  of nkVarSection, nkLetSection: result = evalVar(c, n)
   of nkTryStmt: result = evalTry(c, n)
   of nkRaiseStmt: result = evalRaise(c, n)
   of nkReturnStmt: result = evalReturn(c, n)
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index a1fd73c88..1b218ad7e 100755
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -92,7 +92,7 @@ type
     errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, 
     errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, 
     errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, 
-    errXhasSideEffects, errIteratorExpected, errWrongSymbolX,
+    errXhasSideEffects, errIteratorExpected, errLetNeedsInit, errWrongSymbolX,
     errUser,
     warnCannotOpenFile, 
     warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, 
@@ -320,6 +320,7 @@ const
     errXisNoMacroOrTemplate: "\'$1\' is no macro or template",
     errXhasSideEffects: "\'$1\' can have side effects", 
     errIteratorExpected: "iterator within for loop context expected",
+    errLetNeedsInit: "'let' symbol requires an initialization",
     errWrongSymbolX: "usage of \'$1\' is a user-defined error", 
     errUser: "$1", 
     warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
diff --git a/compiler/nversion.nim b/compiler/nversion.nim
index b0a40f204..a0deda76e 100755
--- a/compiler/nversion.nim
+++ b/compiler/nversion.nim
@@ -18,5 +18,5 @@ const
   VersionPatch* = 13
   VersionAsString* = $VersionMajor & "." & $VersionMinor & "." & $VersionPatch
 
-  RodFileVersion* = "1032"       # modify this if the rod-format changes!
+  RodFileVersion* = "1033"       # modify this if the rod-format changes!
 
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 361c297fb..e3bf3a748 100755
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -1352,7 +1352,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
   of tkConverter: result = parseRoutine(p, nkConverterDef)
   of tkType: result = parseSection(p, nkTypeSection, parseTypeDef)
   of tkConst: result = parseSection(p, nkConstSection, parseConstant)
-  of tkLet: result = parseSection(p, nkLetSection, parseConstant)
+  of tkLet: result = parseSection(p, nkLetSection, parseVariable)
   of tkWhen: result = parseIfOrWhen(p, nkWhenStmt)
   of tkVar: result = parseSection(p, nkVarSection, parseVariable)
   of tkBind: result = parseBind(p)
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 5eb6263e9..d4ea3226d 100755
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -52,6 +52,7 @@ const
     wImportcpp, wImportobjc, wError, wNoInit}
   constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
     wExtern, wImportcpp, wImportobjc, wError}
+  letPragmas* = varPragmas
   procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideEffect,
                       wThread}
   allRoutinePragmas* = procPragmas + iteratorPragmas + lambdaPragmas
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index a38fba907..09539f23d 100755
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -385,7 +385,7 @@ proc lsub(n: PNode): int =
   of nkProcTy: result = lsons(n) + len("proc_")
   of nkEnumTy: result = lsub(n.sons[0]) + lcomma(n, 1) + len("enum_")
   of nkEnumFieldDef: result = lsons(n) + 3
-  of nkVarSection: 
+  of nkVarSection, nkLetSection: 
     if sonsLen(n) > 1: result = maxLineLen + 1
     else: result = lsons(n) + len("var_")
   of nkReturnStmt: result = lsub(n.sons[0]) + len("return_")
@@ -650,7 +650,7 @@ proc gident(g: var TSrcGen, n: PNode) =
   else: 
     t = tkOpr
   put(g, t, s)
-  if (n.kind == nkSym) and (renderIds in g.flags): put(g, tkIntLit, $(n.sym.id))
+  if n.kind == nkSym and renderIds in g.flags: put(g, tkIntLit, $n.sym.id)
   
 proc gsub(g: var TSrcGen, n: PNode, c: TContext) = 
   var 
@@ -825,7 +825,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
   of nkDerefExpr: 
     gsub(g, n.sons[0])
     putWithSpace(g, tkOpr, "^") 
-    # unfortunately this requires a space, because ^. would be only one operator
+    # unfortunately this requires a space, because ^. would be only one opr
   of nkAccQuoted:
     put(g, tkAccent, "`")
     if n.len > 0: gsub(g, n.sons[0])
@@ -960,10 +960,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     initContext(a)
     incl(a.flags, rfInConstExpr)
     gsection(g, n, a, tkConst, "const")
-  of nkVarSection: 
+  of nkVarSection, nkLetSection:
     L = sonsLen(n)
-    if L == 0: return 
-    putWithSpace(g, tkVar, "var")
+    if L == 0: return
+    if n.kind == nkVarSection: putWithSpace(g, tkVar, "var")
+    else: putWithSpace(g, tkLet, "let")
     if L > 1: 
       gcoms(g)
       indentNL(g)
@@ -1084,21 +1085,23 @@ proc renderTree(n: PNode, renderFlags: TRenderFlags = {}): string =
   gsub(g, n)
   result = g.buf
 
-proc renderModule(n: PNode, filename: string, renderFlags: TRenderFlags = {}) = 
-  var 
+proc renderModule(n: PNode, filename: string, 
+                  renderFlags: TRenderFlags = {}) =
+  var
     f: tfile
     g: TSrcGen
   initSrcGen(g, renderFlags)
-  for i in countup(0, sonsLen(n) - 1): 
+  for i in countup(0, sonsLen(n) - 1):
     gsub(g, n.sons[i])
     optNL(g)
     case n.sons[i].kind
-    of nkTypeSection, nkConstSection, nkVarSection, nkCommentStmt: putNL(g)
+    of nkTypeSection, nkConstSection, nkVarSection, nkLetSection,
+       nkCommentStmt: putNL(g)
     else: nil
   gcoms(g)
-  if optStdout in gGlobalOptions: 
+  if optStdout in gGlobalOptions:
     write(stdout, g.buf)
-  elif open(f, filename, fmWrite): 
+  elif open(f, filename, fmWrite):
     write(f, g.buf)
     close(f)
   else:
diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim
index ee56ec0a9..e08d78ae2 100755
--- a/compiler/rodwrite.nim
+++ b/compiler/rodwrite.nim
@@ -531,17 +531,10 @@ proc process(c: PPassContext, n: PNode): PNode =
     if n.sons[bodyPos].kind != nkEmpty or s.magic != mNone or
         sfForward notin s.flags:
       addInterfaceSym(w, s)
-  of nkVarSection:
+  of nkVarSection, nkLetSection, nkConstSection:
     for i in countup(0, sonsLen(n) - 1): 
       var a = n.sons[i]
-      if a.kind == nkCommentStmt: continue 
-      if a.kind != nkIdentDefs: InternalError(a.info, "rodwrite.process")
-      addInterfaceSym(w, a.sons[0].sym)
-  of nkConstSection: 
-    for i in countup(0, sonsLen(n) - 1): 
-      var a = n.sons[i]
-      if a.kind == nkCommentStmt: continue 
-      if a.kind != nkConstDef: InternalError(a.info, "rodwrite.process")
+      if a.kind == nkCommentStmt: continue
       addInterfaceSym(w, a.sons[0].sym)
   of nkTypeSection: 
     for i in countup(0, sonsLen(n) - 1): 
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index d21f557d4..cedce3c9c 100755
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -81,7 +81,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
       result = newSymNode(s, n.info)
   of skMacro: result = semMacroExpr(c, n, s)
   of skTemplate: result = semTemplateExpr(c, n, s)
-  of skVar, skResult:
+  of skVar, skLet, skResult:
     markUsed(n, s)
     # if a proc accesses a global variable, it is not side effect free:
     if sfGlobal in s.flags: incl(c.p.owner.flags, sfSideEffect)
@@ -372,6 +372,7 @@ proc isAssignable(c: PContext, n: PNode): TAssignableResult =
   result = arNone
   case n.kind
   of nkSym:
+    # don't list 'skLet' here:
     if n.sym.kind in {skVar, skResult, skTemp}:
       if c.p.owner.id == n.sym.owner.id: result = arLocalLValue
       else: result = arLValue
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index 488bdcf8a..a81347eda 100755
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -163,7 +163,7 @@ proc semGenericStmt(c: PContext, n: PNode,
       for j in countup(0, L-2): 
         a.sons[j] = semGenericStmt(c, a.sons[j], flags+{withinTypeDesc}, toBind)
       a.sons[L-1] = semGenericStmtScope(c, a.sons[L-1], flags, toBind)
-  of nkVarSection: 
+  of nkVarSection, nkLetSection: 
     for i in countup(0, sonsLen(n) - 1): 
       var a = n.sons[i]
       if a.kind == nkCommentStmt: continue 
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 2818ecbd1..60c0d5913 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -222,15 +222,15 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
     incl(result.flags, sfGlobal)
   else: 
     result = semIdentWithPragma(c, kind, n, {})
-    
-proc semVar(c: PContext, n: PNode): PNode = 
+
+proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = 
   var b: PNode
   result = copyNode(n)
   for i in countup(0, sonsLen(n)-1): 
     var a = n.sons[i]
     if gCmd == cmdIdeTools: suggestStmt(c, a)
     if a.kind == nkCommentStmt: continue 
-    if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): IllFormedAst(a)
+    if a.kind notin {nkIdentDefs, nkVarTuple, nkConstDef}: IllFormedAst(a)
     checkMinSonsLen(a, 3)
     var length = sonsLen(a)
     var typ: PType
@@ -247,22 +247,24 @@ proc semVar(c: PContext, n: PNode): PNode =
       else: typ = def.typ
     else: 
       def = ast.emptyNode
+      if symkind == skLet: GlobalError(a.info, errLetNeedsInit)
+      
     # this can only happen for errornous var statements:
     if typ == nil: continue
-    if not typeAllowed(typ, skVar): 
+    if not typeAllowed(typ, symkind): 
       GlobalError(a.info, errXisNoType, typeToString(typ))
     var tup = skipTypes(typ, {tyGenericInst})
     if a.kind == nkVarTuple: 
       if tup.kind != tyTuple: GlobalError(a.info, errXExpected, "tuple")
-      if length - 2 != sonsLen(tup): 
+      if length-2 != sonsLen(tup): 
         GlobalError(a.info, errWrongNumberOfVariables)
       b = newNodeI(nkVarTuple, a.info)
       newSons(b, length)
-      b.sons[length - 2] = ast.emptyNode # no type desc
-      b.sons[length - 1] = def
+      b.sons[length-2] = ast.emptyNode # no type desc
+      b.sons[length-1] = def
       addSon(result, b)
-    for j in countup(0, length-3): 
-      var v = semIdentDef(c, a.sons[j], skVar)
+    for j in countup(0, length-3):
+      var v = semIdentDef(c, a.sons[j], symkind)
       addInterfaceDecl(c, v)
       if def != nil and def.kind != nkEmpty:
         # this is only needed for the evaluation pass:
@@ -277,7 +279,7 @@ proc semVar(c: PContext, n: PNode): PNode =
       else: 
         v.typ = tup.sons[j]
         b.sons[j] = newSymNode(v)
-
+    
 proc semConst(c: PContext, n: PNode): PNode = 
   result = copyNode(n)
   for i in countup(0, sonsLen(n) - 1): 
@@ -823,7 +825,8 @@ proc SemStmt(c: PContext, n: PNode): PNode =
           of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil
           else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
   of nkRaiseStmt: result = semRaise(c, n)
-  of nkVarSection: result = semVar(c, n)
+  of nkVarSection: result = semVarOrLet(c, n, skVar)
+  of nkLetSection: result = semVarOrLet(c, n, skLet)
   of nkConstSection: result = semConst(c, n)
   of nkTypeSection: result = SemTypeSection(c, n)
   of nkIfStmt: result = SemIf(c, n)
diff --git a/compiler/semthreads.nim b/compiler/semthreads.nim
index dcb38ff90..e402463cf 100755
--- a/compiler/semthreads.nim
+++ b/compiler/semthreads.nim
@@ -107,7 +107,7 @@ proc analyseSym(c: PProcCtx, n: PNode): TThreadOwner =
   result = c.mapping[v.id]
   if result != toUndefined: return
   case v.kind
-  of skVar, skResult:
+  of skVar, skLet, skResult:
     result = toNil
     if sfGlobal in v.flags:
       if sfThread in v.flags: 
@@ -348,7 +348,7 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner =
     var a = analyse(c, n.sons[0])
     if a != toMine: Message(n.info, warnDifferentHeaps)
     result = toVoid
-  of nkVarSection: result = analyseVarSection(c, n)
+  of nkVarSection, nkLetSection: result = analyseVarSection(c, n)
   of nkConstSection: result = analyseConstSection(c, n)
   of nkTypeSection, nkCommentStmt: result = toVoid
   of nkIfStmt, nkWhileStmt, nkTryStmt, nkCaseStmt, nkStmtList, nkBlockStmt, 
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index ca7988aeb..9e78f98e3 100755
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -242,9 +242,10 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
       # process pragmas later, because result.typ has not been set yet
     of skField: pragma(c, result, n.sons[1], fieldPragmas)
     of skVar:   pragma(c, result, n.sons[1], varPragmas)
+    of skLet:   pragma(c, result, n.sons[1], letPragmas)
     of skConst: pragma(c, result, n.sons[1], constPragmas)
     else: nil
-  else: 
+  else:
     result = semIdentVis(c, kind, n, allowed)
   
 proc checkForOverlap(c: PContext, t, ex: PNode, branchIndex: int) = 
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 3c715be6d..5f3becaf6 100755
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -294,7 +294,7 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
   of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: 
     # nothing to be done for leaves:
     result = PTransNode(n)
-  of nkVarSection:
+  of nkVarSection, nkLetSection:
     result = transformVarSection(c, n)
   else:
     result = newTransNode(n)
@@ -518,7 +518,7 @@ proc gatherVars(c: PTransf, n: PNode, marked: var TIntSet, owner: PSym,
     var s = n.sym
     var found = false
     case s.kind
-    of skVar: found = sfGlobal notin s.flags
+    of skVar, skLet: found = sfGlobal notin s.flags
     of skTemp, skForVar, skParam, skResult: found = true
     else: nil
     if found and owner.id != s.owner.id and not ContainsOrIncl(marked, s.id): 
@@ -714,7 +714,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
   of nkConstSection:
     # do not replace ``const c = 3`` with ``const 3 = 3``
     return transformConstSection(c, n)
-  of nkVarSection: 
+  of nkVarSection, nkLetSection:
     if c.inlining > 0: 
       # we need to copy the variables for multiple yield statements:
       result = transformVarSection(c, n)
diff --git a/compiler/types.nim b/compiler/types.nim
index 31c94236a..6ce4c6e48 100755
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -816,7 +816,7 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
   result = a.kind == last
   
 proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
-  assert(kind in {skVar, skConst, skParam, skResult})
+  assert(kind in {skVar, skLet, skConst, skParam, skResult})
   # if we have already checked the type, return true, because we stop the
   # evaluation if something is wrong:
   result = true
diff --git a/doc/manual.txt b/doc/manual.txt
index 36eed6a4a..164410a68 100755
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -1555,7 +1555,6 @@ variables of the same type:
 

 If an initializer is given the type can be omitted: the variable is then of the

 same type as the initializing expression. Variables are always initialized

-

 with a default value if there is no initializing expression. The default

 value depends on the type and is always a zero in binary.

 

@@ -1586,6 +1585,19 @@ The implicit initialization can be avoided for optimization reasons with the
     a {.noInit.}: array [0..1023, char] 

 

 

+

+let statement

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

+

+A `Let`:idx: statement declares new local and global `single assignment`:idx:

+variables and binds a value to them. The syntax is the of the ``var`` 

+statement, except that the keyword ``var`` is replaced by the keyword ``let``.

+Let variables are not l-values and can thus not be passed to ``var`` parameters

+nor can their address be taken. They cannot be assigned new values.

+

+For let variables the same pragmas are available as for ordinary variables.

+

+

 Const section

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

 

diff --git a/doc/tut1.txt b/doc/tut1.txt
index 98ef5629c..546c6e57c 100755
--- a/doc/tut1.txt
+++ b/doc/tut1.txt
@@ -213,6 +213,17 @@ constants:
     z = y + 5 # computations are possible
 
 
+The let statement
+=================
+The ``let`` statement works like the ``var`` statement but the declared 
+symbols are *single assignment* variables: After the initialization their
+value cannot change:
+
+.. code-block::
+  let x = "abc" # introduces a new variable `x` and binds a value to it
+  x = "xyz"     # Illegal: assignment to `x`
+
+
 Control flow statements
 =======================
 
@@ -227,7 +238,7 @@ If statement
 The if statement is one way to branch the control flow:
 
 .. code-block:: nimrod
-  var name = readLine(stdin)
+  let name = readLine(stdin)
   if name == "":
     echo("Poor soul, you lost your name?")
   elif name == "name":
@@ -247,7 +258,7 @@ Another way to branch is provided by the case statement. A case statement is
 a multi-branch:
 
 .. code-block:: nimrod
-  var name = readLine(stdin)
+  let name = readLine(stdin)
   case name
   of "":
     echo("Poor soul, you lost your name?")
@@ -270,7 +281,7 @@ For integers or other ordinal types value ranges are also possible:
   from strutils import parseInt
 
   Echo("A number please: ")
-  var n = parseInt(readLine(stdin))
+  let n = parseInt(readLine(stdin))
   case n
   of 0..2, 4..7: Echo("The number is in the set: {0, 1, 2, 4, 5, 6, 7}")
   of 3, 8: Echo("The number is 3 or 8")
@@ -410,7 +421,7 @@ the next iteration immediately:
 
 .. code-block:: nimrod
   while true:
-    var x = readLine(stdin)
+    let x = readLine(stdin)
     if x == "": continue
     Echo(x)
 
@@ -717,8 +728,8 @@ However, this cannot be done for mutually recursive procedures:
 
 Here ``odd`` depends on ``even`` and vice versa. Thus ``even`` needs to be
 introduced to the compiler before it is completely defined. The syntax for
-such a `forward declaration` is simple: just omit the ``=`` and the procedure's
-body.
+such a `forward declaration`:idx: is simple: just omit the ``=`` and the
+procedure's body.
 
 
 Iterators
@@ -846,7 +857,7 @@ to mark them to be of another integer type:
 
 
 .. code-block:: nimrod
-  var
+  let
     x = 0     # x is of type ``int``
     y = 0'i8  # y is of type ``int8``
     z = 0'i64 # z is of type ``int64``
diff --git a/doc/tut2.txt b/doc/tut2.txt
index 5d757c28b..9017bd7e0 100755
--- a/doc/tut2.txt
+++ b/doc/tut2.txt
@@ -580,13 +580,13 @@ via a special ``:`` syntax:
 .. code-block:: nimrod
 
   template withFile(f: expr, filename: string, mode: TFileMode,
-                    actions: stmt): stmt =
+                    body: stmt): stmt =
     block:
-      var fn = filename
+      let fn = filename
       var f: TFile
       if open(f, fn, mode):
         try:
-          actions
+          body
         finally:
           close(f)
       else:
@@ -596,10 +596,10 @@ via a special ``:`` syntax:
     txt.writeln("line 1")
     txt.writeln("line 2")
   
-In the example the two ``writeln`` statements are bound to the ``actions``
+In the example the two ``writeln`` statements are bound to the ``body``
 parameter. The ``withFile`` template contains boilerplate code and helps to
 avoid a common bug: to forget to close the file. Note how the
-``var fn = filename`` statement ensures that ``filename`` is evaluated only
+``let fn = filename`` statement ensures that ``filename`` is evaluated only
 once.
 
 
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index fbc47fb63..11208e508 100755
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -37,8 +37,8 @@ type
     nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt, 

     nnkAsmStmt, nnkPragma, nnkIfStmt, nnkWhenStmt, 

     nnkForStmt, nnkWhileStmt, nnkCaseStmt, 

-    nnkVarSection, nnkLetSection, nnkConstSection, 

-    nnkConstDef, nnkTypeSection, nnkTypeDef, 

+    nnkTypeSection, nnkVarSection, nnkLetSection, nnkConstSection, 

+    nnkConstDef, nnkTypeDef, 

     nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt, 

     nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, 

     nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt, 

diff --git a/lib/system/threads.nim b/lib/system/threads.nim
index ce07f0c3b..7c26cf8ee 100755
--- a/lib/system/threads.nim
+++ b/lib/system/threads.nim
@@ -360,7 +360,8 @@ proc threadId*[TArg](t: var TThread[TArg]): TThreadId[TArg] {.inline.} =
   result = addr(t)
 
 proc myThreadId*[TArg](): TThreadId[TArg] =
-  ## returns the thread ID of the thread that calls this proc.
+  ## returns the thread ID of the thread that calls this proc. This is unsafe
+  ## because the type ``TArg`` is not checked for consistency!
   result = cast[TThreadId[TArg]](ThreadVarGetValue(globalsSlot))
 
 when false:
diff --git a/tests/reject/tlet.nim b/tests/reject/tlet.nim
new file mode 100644
index 000000000..3d36432fb
--- /dev/null
+++ b/tests/reject/tlet.nim
@@ -0,0 +1,11 @@
+discard """
+  line: "10"
+  errormsg: "'name' cannot be assigned to"
+"""
+
+Echo("What's your name? ")
+let name = readLine(stdin)
+while name == "":
+  Echo("Please tell me your name: ")
+  name = readLine(stdin)
+
diff --git a/tests/reject/tlet2.nim b/tests/reject/tlet2.nim
new file mode 100644
index 000000000..8b1ddf940
--- /dev/null
+++ b/tests/reject/tlet2.nim
@@ -0,0 +1,16 @@
+discard """
+  line: "13"
+  errormsg: "for a 'var' type a variable needs to be passed"
+"""
+
+proc divmod(a, b: int, res, remainder: var int) =
+  res = a div b        # integer division
+  remainder = a mod b  # integer modulo operation
+
+let
+  x = 9
+  y = 3
+divmod(8, 5, x, y) # modifies x and y
+echo(x)
+echo(y)
+
diff --git a/tests/run/tlet.nim b/tests/run/tlet.nim
new file mode 100644
index 000000000..ba355c5d8
--- /dev/null
+++ b/tests/run/tlet.nim
@@ -0,0 +1,19 @@
+discard """
+  output: '''Very funny, your name is name.
+nameabc'''
+"""
+
+proc main =
+  let name = "name"
+  if name == "":
+    echo("Poor soul, you lost your name?")
+  elif name == "name":
+    echo("Very funny, your name is name.")
+  else:
+    Echo("Hi, ", name, "!")
+    
+  let (x, y) = ("abc", name)
+  echo y, x
+
+main()
+
diff --git a/tests/tester.nim b/tests/tester.nim
index d50b4f766..15b1bb045 100755
--- a/tests/tester.nim
+++ b/tests/tester.nim
@@ -103,7 +103,7 @@ proc parseSpec(filename: string): TSpec =
 
 # ----------------------------------------------------------------------------
 
-var
+let
   pegLineError = 
     peg"{[^(]*} '(' {\d+} ', ' \d+ ') ' ('Error'/'Warning') ':' \s* {.*}"
   pegOtherError = peg"'Error:' \s* {.*}"
@@ -111,10 +111,10 @@ var
   pegOfInterest = pegLineError / pegOtherError
 
 proc callCompiler(cmdTemplate, filename, options: string): TSpec =
-  var c = parseCmdLine(cmdTemplate % [options, filename])
+  let c = parseCmdLine(cmdTemplate % [options, filename])
   var p = startProcess(command=c[0], args=c[1.. -1],
                        options={poStdErrToStdOut, poUseShell})
-  var outp = p.outputStream
+  let outp = p.outputStream
   var suc = ""
   var err = ""
   var x = newStringOfCap(120)
@@ -222,7 +222,7 @@ proc cmpMsgs(r: var TResults, expected, given: TSpec, test: string) =
     inc(r.passed)
 
 proc rejectSingleTest(r: var TResults, test, options: string) =
-  var test = test.addFileExt(".nim")
+  let test = test.addFileExt(".nim")
   var t = extractFilename(test)
   inc(r.total)
   echo t
@@ -240,10 +240,10 @@ proc reject(r: var TResults, dir, options: string) =
 
 proc compile(r: var TResults, pattern, options: string) =
   for test in os.walkFiles(pattern):
-    var t = extractFilename(test)
+    let t = extractFilename(test)
     echo t
     inc(r.total)
-    var expected = parseSpec(test)
+    let expected = parseSpec(test)
     if expected.disabled:
       r.addResult(t, "", reIgnored)
       inc(r.skipped)
@@ -253,11 +253,11 @@ proc compile(r: var TResults, pattern, options: string) =
       if not given.err: inc(r.passed)
 
 proc compileSingleTest(r: var TResults, test, options: string) =
-  var test = test.addFileExt(".nim")
-  var t = extractFilename(test)
+  let test = test.addFileExt(".nim")
+  let t = extractFilename(test)
   inc(r.total)
   echo t
-  var given = callCompiler(cmdTemplate, test, options)
+  let given = callCompiler(cmdTemplate, test, options)
   r.addResult(t, given.msg, if given.err: reFailure else: reSuccess)
   if not given.err: inc(r.passed)
 
diff --git a/todo.txt b/todo.txt
index 4211b49de..e8ad9a8dc 100755
--- a/todo.txt
+++ b/todo.txt
@@ -2,7 +2,10 @@ version 0.8.14
 ==============
 
 - bug: compiler uses full file names again
-- stdlib and compiler should not use deprecated endOfFile and readline
+- implicit invokation of `items`/`pairs` seems nice
+- warning for implicit openArray -> varargs convention
+- implement explicit varargs; **but** ``len(varargs)`` problem remains! 
+  --> solve by implicit conversion from varargs to openarray
 
 version 0.9.0
 =============
@@ -11,18 +14,13 @@ version 0.9.0
   escape analysis for string/seq seems to be easy to do too
 - dead code elim for JS backend; 'of' operator for JS backend
 - test the sort implementation again
-- 'let x = y'
 - const ptr/ref
 - unsigned ints and bignums; requires abstract integer literal type: 
   use tyInt+node for that
 - implement the high level optimizer
-- warning for implicit openArray -> varargs convention
-- implement explicit varargs; **but** ``len(varargs)`` problem remains! 
-  --> solve by implicit conversion from varargs to openarray
 - change overloading resolution
 - implement closures; implement proper coroutines
 - implement ``partial`` pragma for partial evaluation
-- implicit invokation of `items`/`pairs` seems nice
 - we need to support iteration of 2 different data structures in parallel
 - make exceptions compatible with C++ exceptions
 - ``=`` should be overloadable; requires specialization for ``=``
@@ -52,7 +50,6 @@ Bugs
 - bug: stress testing basic method example (eval example) 
   without ``-d:release`` leaks memory; good way to figure out how a 
   fixed amount of stack can hold an arbitrary number of GC roots!
-- bug: osproc.execProcess() should raise an exception if the exit code is not 0
 
 
 version 0.9.XX
diff --git a/web/news.txt b/web/news.txt
index 6ef636cf6..a6405eced 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -74,6 +74,7 @@ Language Additions
   heart's content.
 - ``bind`` (used for symbol binding in templates and generics) is now a
   declarative statement.
+- Nimrod now supports single assignment variables via the ``let`` statement.
 - The slice assignment ``a[i..j] = b`` where ``a`` is a sequence or string
   now supports *splicing*.
 
@@ -114,7 +115,6 @@ Compiler Additions
 Library Additions
 -----------------
 
-- Added ``system.mainThreadId``.
 - Added ``system.allocShared``, ``system.allocShared0``, 
   ``system.deallocShared``, ``system.reallocShared``.
 - Slicing as implemented by the system module now supports *splicing*.