summary refs log tree commit diff stats
path: root/compiler/semstmts.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/semstmts.nim')
-rw-r--r--compiler/semstmts.nim563
1 files changed, 239 insertions, 324 deletions
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index d27247158..2c3adfeda 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -10,54 +10,10 @@
 ## this module does the semantic checking of statements
 #  included from sem.nim
 
+var EnforceVoidContext = PType(kind: tyStmt)
+
 proc semCommand(c: PContext, n: PNode): PNode =
   result = semExprNoType(c, n)
-
-proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
-  # If semCheck is set to false, ``when`` will return the verbatim AST of
-  # the correct branch. Otherwise the AST will be passed through semStmt.
-  result = nil
-  
-  template setResult(e: expr) =
-    if semCheck: result = semStmt(c, e) # do not open a new scope!
-    else: result = e
-
-  for i in countup(0, sonsLen(n) - 1): 
-    var it = n.sons[i]
-    case it.kind
-    of nkElifBranch, nkElifExpr: 
-      checkSonsLen(it, 2)
-      var e = semConstExpr(c, it.sons[0])
-      if e.kind != nkIntLit: InternalError(n.info, "semWhen")
-      elif e.intVal != 0 and result == nil:
-        setResult(it.sons[1]) 
-    of nkElse, nkElseExpr:
-      checkSonsLen(it, 1)
-      if result == nil: 
-        setResult(it.sons[0])
-    else: illFormedAst(n)
-  if result == nil: 
-    result = newNodeI(nkNilLit, n.info) 
-  # The ``when`` statement implements the mechanism for platform dependent
-  # code. Thus we try to ensure here consistent ID allocation after the
-  # ``when`` statement.
-  IDsynchronizationPoint(200)
-
-proc semIf(c: PContext, n: PNode): PNode = 
-  result = n
-  for i in countup(0, sonsLen(n) - 1): 
-    var it = n.sons[i]
-    case it.kind
-    of nkElifBranch: 
-      checkSonsLen(it, 2)
-      it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
-      openScope(c.tab)
-      it.sons[1] = semStmt(c, it.sons[1])
-      closeScope(c.tab)
-    of nkElse: 
-      if sonsLen(it) == 1: it.sons[0] = semStmtScope(c, it.sons[0])
-      else: illFormedAst(it)
-    else: illFormedAst(n)
   
 proc semDiscard(c: PContext, n: PNode): PNode = 
   result = n
@@ -86,21 +42,6 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode =
   elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0): 
     localError(n.info, errInvalidControlFlowX, 
                renderTree(n, {renderNoComments}))
-  
-proc semBlock(c: PContext, n: PNode): PNode = 
-  result = n
-  Inc(c.p.nestedBlockCounter)
-  checkSonsLen(n, 2)
-  openScope(c.tab)            # BUGFIX: label is in the scope of block!
-  if n.sons[0].kind != nkEmpty:
-    var labl = newSymG(skLabel, n.sons[0], c)
-    if sfGenSym notin labl.flags:
-      addDecl(c, labl)
-      n.sons[0] = newSymNode(labl, n.sons[0].info)
-    suggestSym(n.sons[0], labl)
-  n.sons[1] = semStmt(c, n.sons[1])
-  closeScope(c.tab)
-  Dec(c.p.nestedBlockCounter)
 
 proc semAsm(con: PContext, n: PNode): PNode = 
   checkSonsLen(n, 2)
@@ -117,6 +58,8 @@ proc semWhile(c: PContext, n: PNode): PNode =
   n.sons[1] = semStmt(c, n.sons[1])
   dec(c.p.nestedLoopCounter)
   closeScope(c.tab)
+  if n.sons[1].typ == EnforceVoidContext:
+    result.typ = EnforceVoidContext
 
 proc toCover(t: PType): biggestInt = 
   var t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
@@ -125,20 +68,126 @@ proc toCover(t: PType): biggestInt =
   else:
     result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc}))
 
-proc semCase(c: PContext, n: PNode): PNode = 
-  # check selector:
+proc performProcvarCheck(c: PContext, n: PNode, s: PSym) =
+  var smoduleId = getModule(s).id
+  if sfProcVar notin s.flags and s.typ.callConv == ccDefault and
+      smoduleId != c.module.id and smoduleId != c.friendModule.id: 
+    LocalError(n.info, errXCannotBePassedToProcVar, s.name.s)
+
+proc semProcvarCheck(c: PContext, n: PNode) =
+  let n = n.skipConv
+  if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skIterator,
+                                        skConverter}:
+    performProcvarCheck(c, n, n.sym)
+
+proc semProc(c: PContext, n: PNode): PNode
+
+include semdestruct
+
+proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} =
+  if efAllowDestructor notin flags and n.kind in nkCallKinds+{nkObjConstr}:
+    if instantiateDestructor(c, n.typ):
+      LocalError(n.info, errGenerated,
+        "usage of a type with a destructor in a non destructible context")
+
+proc newDeref(n: PNode): PNode {.inline.} =  
+  result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
+  addSon(result, n)
+
+proc semExprBranch(c: PContext, n: PNode): PNode =
+  result = semExpr(c, n)
+  if result.typ != nil:
+    # XXX tyGenericInst here?
+    semProcvarCheck(c, result)
+    if result.typ.kind == tyVar: result = newDeref(result)
+    semDestructorCheck(c, result, {})
+
+proc semExprBranchScope(c: PContext, n: PNode): PNode =
+  openScope(c.tab)
+  result = semExprBranch(c, n)
+  closeScope(c.tab)
+
+const
+  skipForDiscardable = {nkIfStmt, nkIfExpr, nkCaseStmt, nkOfBranch,
+    nkElse, nkStmtListExpr, nkTryStmt, nkFinally, nkExceptBranch,
+    nkElifBranch, nkElifExpr, nkElseExpr, nkBlockStmt, nkBlockExpr}
+
+proc ImplicitlyDiscardable(n: PNode): bool =
+  var n = n
+  while n.kind in skipForDiscardable: n = n.lastSon
+  result = isCallExpr(n) and n.sons[0].kind == nkSym and 
+           sfDiscardable in n.sons[0].sym.flags
+
+proc fixNilType(n: PNode) =
+  if isAtom(n):
+    if n.kind != nkNilLit and n.typ != nil:
+      localError(n.info, errDiscardValue)
+  elif n.kind in {nkStmtList, nkStmtListExpr}:
+    n.kind = nkStmtList
+    for it in n: fixNilType(it)
+  n.typ = nil
+
+proc discardCheck(result: PNode) =
+  if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}:
+    if result.kind == nkNilLit:
+      result.typ = nil
+    elif ImplicitlyDiscardable(result):
+      var n = result
+      result.typ = nil
+      while n.kind in skipForDiscardable:
+        n = n.lastSon
+        n.typ = nil
+    elif result.typ.kind != tyError and gCmd != cmdInteractive:
+      if result.typ.kind == tyNil:
+        fixNilType(result)
+      else:
+        localError(result.info, errDiscardValue)
+
+proc semIf(c: PContext, n: PNode): PNode = 
+  result = n
+  var typ = CommonTypeBegin
+  var hasElse = false
+  for i in countup(0, sonsLen(n) - 1): 
+    var it = n.sons[i]
+    if it.len == 2:
+      when newScopeForIf: openScope(c.tab)
+      it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
+      when not newScopeForIf: openScope(c.tab)
+      it.sons[1] = semExprBranch(c, it.sons[1])
+      typ = commonType(typ, it.sons[1].typ)
+      closeScope(c.tab)
+    elif it.len == 1:
+      hasElse = true
+      it.sons[0] = semExprBranchScope(c, it.sons[0])
+      typ = commonType(typ, it.sons[0].typ)
+    else: illFormedAst(it)
+  if isEmptyType(typ) or typ.kind == tyNil or not hasElse:
+    for it in n: discardCheck(it.lastSon)
+    result.kind = nkIfStmt
+    # propagate any enforced VoidContext:
+    if typ == EnforceVoidContext: result.typ = EnforceVoidContext
+  else:
+    for it in n:
+      let j = it.len-1
+      it.sons[j] = fitNode(c, typ, it.sons[j])
+    result.kind = nkIfExpr
+    result.typ = typ
+
+proc semCase(c: PContext, n: PNode): PNode =
   result = n
   checkMinSonsLen(n, 2)
   openScope(c.tab)
   n.sons[0] = semExprWithType(c, n.sons[0])
   var chckCovered = false
   var covered: biggestint = 0
+  var typ = CommonTypeBegin
+  var hasElse = false
   case skipTypes(n.sons[0].Typ, abstractVarRange-{tyTypeDesc}).Kind
-  of tyInt..tyInt64, tyChar, tyEnum: 
+  of tyInt..tyInt64, tyChar, tyEnum:
     chckCovered = true
-  of tyFloat..tyFloat128, tyString, tyError: 
+  of tyFloat..tyFloat128, tyString, tyError:
     nil
-  else: 
+  else:
     LocalError(n.info, errSelectorMustBeOfCertainTypes)
     return
   for i in countup(1, sonsLen(n) - 1): 
@@ -147,21 +196,89 @@ proc semCase(c: PContext, n: PNode): PNode =
     of nkOfBranch: 
       checkMinSonsLen(x, 2)
       semCaseBranch(c, n, x, i, covered)
-      var length = sonsLen(x)
-      x.sons[length - 1] = semStmtScope(c, x.sons[length - 1])
-    of nkElifBranch: 
+      var last = sonsLen(x)-1
+      x.sons[last] = semExprBranchScope(c, x.sons[last])
+      typ = commonType(typ, x.sons[last].typ)
+    of nkElifBranch:
       chckCovered = false
       checkSonsLen(x, 2)
+      when newScopeForIf: openScope(c.tab)
       x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
-      x.sons[1] = semStmtScope(c, x.sons[1])
-    of nkElse: 
+      when not newScopeForIf: openScope(c.tab)
+      x.sons[1] = semExprBranch(c, x.sons[1])
+      typ = commonType(typ, x.sons[1].typ)
+      closeScope(c.tab)
+    of nkElse:
       chckCovered = false
       checkSonsLen(x, 1)
-      x.sons[0] = semStmtScope(c, x.sons[0])
-    else: illFormedAst(x)
-  if chckCovered and (covered != toCover(n.sons[0].typ)): 
-    localError(n.info, errNotAllCasesCovered)
+      x.sons[0] = semExprBranchScope(c, x.sons[0])
+      typ = commonType(typ, x.sons[0].typ)
+      hasElse = true
+    else:
+      illFormedAst(x)
+  if chckCovered:
+    if covered == toCover(n.sons[0].typ):
+      hasElse = true
+    else:
+      localError(n.info, errNotAllCasesCovered)
   closeScope(c.tab)
+  if isEmptyType(typ) or typ.kind == tyNil or not hasElse:
+    for i in 1..n.len-1: discardCheck(n.sons[i].lastSon)
+    # propagate any enforced VoidContext:
+    if typ == EnforceVoidContext:
+      result.typ = EnforceVoidContext
+  else:
+    for i in 1..n.len-1:
+      var it = n.sons[i]
+      let j = it.len-1
+      it.sons[j] = fitNode(c, typ, it.sons[j])
+    result.typ = typ
+
+proc semTry(c: PContext, n: PNode): PNode =
+  result = n
+  inc c.p.inTryStmt
+  checkMinSonsLen(n, 2)
+  var typ = CommonTypeBegin
+  n.sons[0] = semExprBranchScope(c, n.sons[0])
+  typ = commonType(typ, n.sons[0].typ)
+  var check = initIntSet()
+  for i in countup(1, sonsLen(n) - 1): 
+    var a = n.sons[i]
+    checkMinSonsLen(a, 1)
+    var length = sonsLen(a)
+    if a.kind == nkExceptBranch:
+      # XXX what does this do? so that ``except [a, b, c]`` is supported?
+      if length == 2 and a.sons[0].kind == nkBracket:
+        a.sons[0..0] = a.sons[0].sons
+        length = a.sonsLen
+
+      for j in countup(0, length-2):
+        var typ = semTypeNode(c, a.sons[j], nil)
+        if typ.kind == tyRef: typ = typ.sons[0]
+        if typ.kind != tyObject:
+          LocalError(a.sons[j].info, errExprCannotBeRaised)
+        a.sons[j] = newNodeI(nkType, a.sons[j].info)
+        a.sons[j].typ = typ
+        if ContainsOrIncl(check, typ.id):
+          localError(a.sons[j].info, errExceptionAlreadyHandled)
+    elif a.kind != nkFinally: 
+      illFormedAst(n)
+    # last child of an nkExcept/nkFinally branch is a statement:
+    a.sons[length-1] = semExprBranchScope(c, a.sons[length-1])
+    typ = commonType(typ, a.sons[length-1].typ)
+  dec c.p.inTryStmt
+  if isEmptyType(typ) or typ.kind == tyNil:
+    discardCheck(n.sons[0])
+    for i in 1..n.len-1: discardCheck(n.sons[i].lastSon)
+    if typ == EnforceVoidContext:
+      result.typ = EnforceVoidContext
+  else:
+    n.sons[0] = fitNode(c, typ, n.sons[0])
+    for i in 1..n.len-1:
+      var it = n.sons[i]
+      let j = it.len-1
+      it.sons[j] = fitNode(c, typ, it.sons[j])
+    result.typ = typ
   
 proc fitRemoveHiddenConv(c: PContext, typ: Ptype, n: PNode): PNode = 
   result = fitNode(c, typ, n)
@@ -484,10 +601,6 @@ proc semForVars(c: PContext, n: PNode): PNode =
   n.sons[length-1] = SemStmt(c, n.sons[length-1])
   Dec(c.p.nestedLoopCounter)
 
-proc newDeref(n: PNode): PNode {.inline.} =  
-  result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
-  addSon(result, n)
-
 proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
   result = newNodeI(nkCall, arg.info)
   result.add(newIdentNode(it.getIdent, arg.info))
@@ -524,6 +637,9 @@ proc semFor(c: PContext, n: PNode): PNode =
       result = semForFields(c, n, call.sons[0].sym.magic)
   else:
     result = semForVars(c, n)
+  # propagate any enforced VoidContext:
+  if n.sons[length-1].typ == EnforceVoidContext:
+    result.typ = EnforceVoidContext
   closeScope(c.tab)
 
 proc semRaise(c: PContext, n: PNode): PNode = 
@@ -535,36 +651,6 @@ proc semRaise(c: PContext, n: PNode): PNode =
     if typ.kind != tyRef or typ.sons[0].kind != tyObject: 
       localError(n.info, errExprCannotBeRaised)
 
-proc semTry(c: PContext, n: PNode): PNode = 
-  result = n
-  inc c.p.inTryStmt
-  checkMinSonsLen(n, 2)
-  n.sons[0] = semStmtScope(c, n.sons[0])
-  var check = initIntSet()
-  for i in countup(1, sonsLen(n) - 1): 
-    var a = n.sons[i]
-    checkMinSonsLen(a, 1)
-    var length = sonsLen(a)
-    if a.kind == nkExceptBranch:
-      if length == 2 and a.sons[0].kind == nkBracket:
-        a.sons[0..0] = a.sons[0].sons
-        length = a.sonsLen
-
-      for j in countup(0, length - 2):
-        var typ = semTypeNode(c, a.sons[j], nil)
-        if typ.kind == tyRef: typ = typ.sons[0]
-        if typ.kind != tyObject:
-          LocalError(a.sons[j].info, errExprCannotBeRaised)
-        a.sons[j] = newNodeI(nkType, a.sons[j].info)
-        a.sons[j].typ = typ
-        if ContainsOrIncl(check, typ.id):
-          localError(a.sons[j].info, errExceptionAlreadyHandled)
-    elif a.kind != nkFinally: 
-      illFormedAst(n) 
-    # last child of an nkExcept/nkFinally branch is a statement:
-    a.sons[length - 1] = semStmtScope(c, a.sons[length - 1])
-  dec c.p.inTryStmt
-
 proc addGenericParamListToScope(c: PContext, n: PNode) =
   if n.kind != nkGenericParams: illFormedAst(n)
   for i in countup(0, sonsLen(n)-1):
@@ -675,6 +761,9 @@ proc SemTypeSection(c: PContext, n: PNode): PNode =
 
 proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) =
   s.typ = semProcTypeNode(c, n, genericParams, nil, s.kind)
+  if s.kind notin {skMacro, skTemplate}:
+    if s.typ.sons[0] != nil and s.typ.sons[0].kind == tyStmt:
+      localError(n.info, errGenerated, "invalid return type: 'stmt'")
 
 proc addParams(c: PContext, n: PNode, kind: TSymKind) = 
   for i in countup(1, sonsLen(n)-1): 
@@ -789,22 +878,6 @@ proc activate(c: PContext, n: PNode) =
     else:
       nil
 
-proc instantiateDestructor*(c: PContext, typ: PType): bool
-
-proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
-  let t = s.typ.sons[1].skipTypes({tyVar})
-  t.destructor = s
-  # automatically insert calls to base classes' destructors
-  if n.sons[bodyPos].kind != nkEmpty:
-    for i in countup(0, t.sonsLen - 1):
-      # when inheriting directly from object
-      # there will be a single nil son
-      if t.sons[i] == nil: continue
-      if instantiateDestructor(c, t.sons[i]):
-        n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[
-            useSym(t.sons[i].destructor),
-            n.sons[paramsPos][1][0]]))
-
 proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
   if s.typ.sons[0] != nil and
       (s.kind != skIterator or s.typ.callConv == ccClosure):
@@ -1015,203 +1088,34 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
     result = newNodeI(nkDiscardStmt, n.info, 1)
     result.sons[0] = emptyNode
 
-# special marker values that indicates that we are
-# 1) AnalyzingDestructor: currently analyzing the type for destructor 
-# generation (needed for recursive types)
-# 2) DestructorIsTrivial: completed the analysis before and determined
-# that the type has a trivial destructor
-var AnalyzingDestructor, DestructorIsTrivial: PSym
-new(AnalyzingDestructor)
-new(DestructorIsTrivial)
-
-var
-  destructorName = getIdent"destroy_"
-  destructorParam = getIdent"this_"
-  destructorPragma = newIdentNode(getIdent"destructor", UnknownLineInfo())
-  rangeDestructorProc*: PSym
-
-proc destroyField(c: PContext, field: PSym, holder: PNode): PNode =
-  if instantiateDestructor(c, field.typ):
-    result = newNode(nkCall, field.info, @[
-      useSym(field.typ.destructor),
-      newNode(nkDotExpr, field.info, @[holder, useSym(field)])])
-
-proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
-  var nonTrivialFields = 0
-  result = newNode(nkCaseStmt, n.info, @[])
-  # case x.kind
-  result.addSon(newNode(nkDotExpr, n.info, @[holder, n.sons[0]]))
-  for i in countup(1, n.len - 1):
-    # of A, B:
-    var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2])
-    let recList = n[i].lastSon
-    var destroyRecList = newNode(nkStmtList, n[i].info, @[])
-    template addField(f: expr): stmt =
-      let stmt = destroyField(c, f, holder)
-      if stmt != nil:
-        destroyRecList.addSon(stmt)
-        inc nonTrivialFields
-        
-    case recList.kind
-    of nkSym:
-      addField(recList.sym)
-    of nkRecList:
-      for j in countup(0, recList.len - 1):
-        addField(recList[j].sym)
-    else:
-      internalAssert false
-      
-    caseBranch.addSon(destroyRecList)
-    result.addSon(caseBranch)
-  # maybe no fields were destroyed?
-  if nonTrivialFields == 0:
-    result = nil
- 
-proc generateDestructor(c: PContext, t: PType): PNode =
-  ## generate a destructor for a user-defined object or tuple type
-  ## returns nil if the destructor turns out to be trivial
-  
-  template addLine(e: expr): stmt =
-    if result == nil: result = newNode(nkStmtList)
-    result.addSon(e)
-
-  # XXX: This may be true for some C-imported types such as
-  # Tposix_spawnattr
-  if t.n == nil or t.n.sons == nil: return
-  internalAssert t.n.kind == nkRecList
-  let destructedObj = newIdentNode(destructorParam, UnknownLineInfo())
-  # call the destructods of all fields
-  for s in countup(0, t.n.sons.len - 1):
-    case t.n.sons[s].kind
-    of nkRecCase:
-      let stmt = destroyCase(c, t.n.sons[s], destructedObj)
-      if stmt != nil: addLine(stmt)
-    of nkSym:
-      let stmt = destroyField(c, t.n.sons[s].sym, destructedObj)
-      if stmt != nil: addLine(stmt)
-    else:
-      internalAssert false
-
-  # base classes' destructors will be automatically called by
-  # semProcAux for both auto-generated and user-defined destructors
-
-proc instantiateDestructor*(c: PContext, typ: PType): bool =
-  # returns true if the type already had a user-defined
-  # destructor or if the compiler generated a default
-  # member-wise one
-  var t = skipTypes(typ, {tyConst, tyMutable})
-  
-  if t.destructor != nil:
-    # XXX: This is not entirely correct for recursive types, but we need
-    # it temporarily to hide the "destroy is already defined" problem
-    return t.destructor notin [AnalyzingDestructor, DestructorIsTrivial]
-  
-  case t.kind
-  of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs:
-    if instantiateDestructor(c, t.sons[0]):
-      if rangeDestructorProc == nil:
-        rangeDestructorProc = SymtabGet(c.tab, getIdent"nimDestroyRange")
-      t.destructor = rangeDestructorProc
-      return true
-    else:
-      return false
-  of tyTuple, tyObject:
-    t.destructor = AnalyzingDestructor
-    let generated = generateDestructor(c, t)
-    if generated != nil:
-      internalAssert t.sym != nil
-      var i = t.sym.info
-      let fullDef = newNode(nkProcDef, i, @[
-        newIdentNode(destructorName, i),
-        emptyNode,
-        emptyNode,
-        newNode(nkFormalParams, i, @[
-          emptyNode,
-          newNode(nkIdentDefs, i, @[
-            newIdentNode(destructorParam, i),
-            useSym(t.sym),
-            emptyNode]),
-          ]),
-        newNode(nkPragma, i, @[destructorPragma]),
-        emptyNode,
-        generated
-        ])
-      discard semProc(c, fullDef)
-      internalAssert t.destructor != nil
-      return true
+proc usesResult(n: PNode): bool =
+  # nkStmtList(expr) properly propagates the void context,
+  # so we don't need to process that all over again:
+  if n.kind notin {nkStmtList, nkStmtListExpr} + procDefs:
+    if isAtom(n):
+      result = n.kind == nkSym and n.sym.kind == skResult
+    elif n.kind == nkReturnStmt:
+      result = true
     else:
-      t.destructor = DestructorIsTrivial
-      return false
-  else:
-    return false
-
-proc insertDestructors(c: PContext,
-                       varSection: PNode): tuple[outer, inner: PNode] =
-  # Accepts a var or let section.
-  #
-  # When a var section has variables with destructors
-  # the var section is split up and finally blocks are inserted
-  # immediately after all "destructable" vars
-  #
-  # In case there were no destrucable variables, the proc returns
-  # (nil, nil) and the enclosing stmt-list requires no modifications.
-  #
-  # Otherwise, after the try blocks are created, the rest of the enclosing
-  # stmt-list should be inserted in the most `inner` such block (corresponding
-  # to the last variable).
-  #
-  # `outer` is a statement list that should replace the original var section.
-  # It will include the new truncated var section followed by the outermost
-  # try block.
-  let totalVars = varSection.sonsLen
-  for j in countup(0, totalVars - 1):
-    let
-      varId = varSection[j][0]
-      varTyp = varId.sym.typ
-      info = varId.info
-
-    if varTyp != nil and instantiateDestructor(c, varTyp) and 
-        sfGlobal notin varId.sym.flags:
-      var tryStmt = newNodeI(nkTryStmt, info)
-
-      if j < totalVars - 1:
-        var remainingVars = newNodeI(varSection.kind, info)
-        remainingVars.sons = varSection.sons[(j+1)..(-1)]
-        let (outer, inner) = insertDestructors(c, remainingVars)
-        if outer != nil:
-          tryStmt.addSon(outer)
-          result.inner = inner
-        else:
-          result.inner = newNodeI(nkStmtList, info)
-          result.inner.addSon(remainingVars)
-          tryStmt.addSon(result.inner)
-      else:
-        result.inner = newNodeI(nkStmtList, info)
-        tryStmt.addSon(result.inner)
-
-      tryStmt.addSon(
-        newNode(nkFinally, info, @[
-          semStmt(c, newNode(nkCall, info, @[
-            useSym(varTyp.destructor),
-            useSym(varId.sym)]))]))
-
-      result.outer = newNodeI(nkStmtList, info)
-      varSection.sons.setLen(j+1)
-      result.outer.addSon(varSection)
-      result.outer.addSon(tryStmt)
-
-      return
-
-proc ImplicitlyDiscardable(n: PNode): bool =
-  result = isCallExpr(n) and n.sons[0].kind == nkSym and 
-           sfDiscardable in n.sons[0].sym.flags
+      for c in n:
+        if usesResult(c): return true
 
 proc semStmtList(c: PContext, n: PNode): PNode =
   # these must be last statements in a block:
   const
     LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
   result = n
+  result.kind = nkStmtList
   var length = sonsLen(n)
+  var voidContext = false
+  var last = length-1
+  # by not allowing for nkCommentStmt etc. we ensure nkStmtListExpr actually
+  # really *ends* in the expression that produces the type: The compiler now
+  # relies on this fact and it's too much effort to change that. And arguably
+  #  'R(); #comment' shouldn't produce R's type anyway.
+  #while last > 0 and n.sons[last].kind in {nkPragma, nkCommentStmt,
+  #                                         nkNilLit, nkEmpty}:
+  #  dec last
   for i in countup(0, length - 1):
     case n.sons[i].kind
     of nkFinally, nkExceptBranch:
@@ -1233,7 +1137,16 @@ proc semStmtList(c: PContext, n: PNode): PNode =
       n.sons.setLen(i+1)
       return
     else:
-      n.sons[i] = semStmt(c, n.sons[i])
+      n.sons[i] = semExpr(c, n.sons[i])
+      if n.sons[i].typ == EnforceVoidContext or usesResult(n.sons[i]):
+        voidContext = true
+        n.typ = EnforceVoidContext
+      elif i != last or voidContext:
+        discardCheck(n.sons[i])
+      else:
+        n.typ = n.sons[i].typ
+        if not isEmptyType(n.typ):
+          n.kind = nkStmtListExpr
       case n.sons[i].kind
       of nkVarSection, nkLetSection:
         let (outer, inner) = insertDestructors(c, n.sons[i])
@@ -1249,15 +1162,17 @@ proc semStmtList(c: PContext, n: PNode): PNode =
           of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil
           else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
       else: nil
-  
-  # a statement list (s; e) has the type 'e':
-  if result.kind == nkStmtList and result.len > 0:
-    var lastStmt = lastSon(result)
-    if lastStmt.kind != nkNilLit and not ImplicitlyDiscardable(lastStmt):
-      result.typ = lastStmt.typ
-      #localError(lastStmt.info, errGenerated,
-      #  "Last expression must be explicitly returned if it " &
-      #  "is discardable or discarded")
+  if result.len == 1:
+    result = result.sons[0]
+  when false:
+    # a statement list (s; e) has the type 'e':
+    if result.kind == nkStmtList and result.len > 0:
+      var lastStmt = lastSon(result)
+      if lastStmt.kind != nkNilLit and not ImplicitlyDiscardable(lastStmt):
+        result.typ = lastStmt.typ
+        #localError(lastStmt.info, errGenerated,
+        #  "Last expression must be explicitly returned if it " &
+        #  "is discardable or discarded")
 
 proc SemStmt(c: PContext, n: PNode): PNode = 
   # now: simply an alias: