summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2012-09-13 08:33:55 +0200
committerAraq <rumpf_a@web.de>2012-09-13 08:33:55 +0200
commit23c3af80f60c1da49a1c979642e712358d960301 (patch)
tree1d8fa786c0bfb0ef1481cdfe259d2bbc2d884163 /compiler
parent8c8400ea3aa8a785aa24127602de917c21c20ffb (diff)
downloadNim-23c3af80f60c1da49a1c979642e712358d960301.tar.gz
semExpr/semStmt merged
Diffstat (limited to 'compiler')
-rwxr-xr-xcompiler/ast.nim4
-rwxr-xr-xcompiler/msgs.nim2
-rwxr-xr-xcompiler/sem.nim11
-rwxr-xr-xcompiler/semexprs.nim112
-rw-r--r--compiler/sempass2.nim22
-rwxr-xr-xcompiler/semstmts.nim202
6 files changed, 187 insertions, 166 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 2b0fe6470..434e09dd9 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -1197,3 +1197,7 @@ iterator items*(n: PNode): PNode =
 
 proc isAtom*(n: PNode): bool {.inline.} =
   result = n.kind >= nkNone and n.kind <= nkNilLit
+
+proc isEmptyType*(t: PType): bool {.inline.} =
+  ## 'void' and 'stmt' types are often equivalent to 'nil' these days:
+  result = t == nil or t.kind in {tyEmpty, tyStmt}
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 74267c059..0ad79f597 100755
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -226,7 +226,7 @@ const
     errOrdXMustNotBeNegative: "ord($1) must not be negative", 
     errLenXinvalid: "len($1) must be less than 32768",
     errWrongNumberOfVariables: "wrong number of variables", 
-    errExprCannotBeRaised: "only objects can be raised", 
+    errExprCannotBeRaised: "only a 'ref object' can be raised", 
     errBreakOnlyInLoop: "'break' only allowed in loop construct", 
     errTypeXhasUnknownSize: "type \'$1\' has unknown size", 
     errConstNeedsConstExpr: "a constant can only be initialized with a constant expression", 
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 2c65f55a0..1ccc29577 100755
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -22,12 +22,17 @@ proc semPass*(): TPass
 
 type 
   TExprFlag = enum 
-    efLValue, efWantIterator, efInTypeof
+    efLValue, efWantIterator, efInTypeof, efWantStmt
   TExprFlags = set[TExprFlag]
 
 proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
 proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
+proc semExprNoType(c: PContext, n: PNode): PNode
+proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
+
 proc fitNode(c: PContext, formal: PType, arg: PNode): PNode
+proc changeType(n: PNode, newType: PType)
+
 proc semLambda(c: PContext, n: PNode): PNode
 proc semTypeNode(c: PContext, n: PNode, prev: PType): PType
 proc semStmt(c: PContext, n: PNode): PNode
@@ -83,6 +88,8 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode
 
 proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, 
                   semCheck: bool = true): PNode
+proc semMacroStmt(c: PContext, n: PNode, flags: TExprFlags,
+                  semCheck = true): PNode
 proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
 
 proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode
@@ -156,7 +163,7 @@ proc semConstBoolExpr(c: PContext, n: PNode): PNode =
     LocalError(n.info, errConstExprExpected)
     result = nn
 
-include semtypes, semtempl, semexprs, semgnrc, semstmts
+include semtypes, semtempl, semgnrc, semstmts, semexprs
 
 proc addCodeForGenerics(c: PContext, n: PNode) = 
   for i in countup(c.generics.lastGenericIdx, Len(c.generics.generics) - 1):
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 521de8c23..e53db7cec 100755
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -706,10 +706,14 @@ proc semExprNoType(c: PContext, n: PNode): PNode =
   proc ImplicitelyDiscardable(n: PNode): bool {.inline.} =
     result = isCallExpr(n) and n.sons[0].kind == nkSym and 
              sfDiscardable in n.sons[0].sym.flags
-  result = semExpr(c, n)
+  result = semExpr(c, n, {efWantStmt})
   if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}:
     if gCmd == cmdInteractive:
       result = buildEchoStmt(c, result)
+    elif result.kind == nkNilLit:
+      # XXX too much work and fixing would break bootstrapping:
+      #Message(n.info, warnNilStatement)
+      result.typ = nil
     elif not ImplicitelyDiscardable(result) and result.typ.kind != tyError:
       localError(n.info, errDiscardValue)
   
@@ -1041,6 +1045,60 @@ proc semAsgn(c: PContext, n: PNode): PNode =
     asgnToResultVar(c, n, n.sons[0], n.sons[1])
   result = n
 
+proc SemReturn(c: PContext, n: PNode): PNode = 
+  result = n
+  checkSonsLen(n, 1)
+  if c.p.owner.kind notin {skConverter, skMethod, skProc, skMacro}:
+    LocalError(n.info, errXNotAllowedHere, "\'return\'")
+  elif n.sons[0].kind != nkEmpty:
+    # transform ``return expr`` to ``result = expr; return``
+    if c.p.resultSym != nil: 
+      var a = newNodeI(nkAsgn, n.sons[0].info)
+      addSon(a, newSymNode(c.p.resultSym))
+      addSon(a, n.sons[0])
+      n.sons[0] = semAsgn(c, a)
+      # optimize away ``result = result``:
+      if n[0][1].kind == nkSym and n[0][1].sym.kind == skResult: 
+        n.sons[0] = ast.emptyNode
+    else:
+      LocalError(n.info, errNoReturnTypeDeclared)
+
+proc SemYieldVarResult(c: PContext, n: PNode, restype: PType) =
+  var t = skipTypes(restype, {tyGenericInst})
+  case t.kind
+  of tyVar:
+    n.sons[0] = takeImplicitAddr(c, n.sons[0])
+  of tyTuple:
+    for i in 0.. <t.sonsLen:
+      var e = skipTypes(t.sons[i], {tyGenericInst})
+      if e.kind == tyVar:
+        if n.sons[0].kind == nkPar:
+          n.sons[0].sons[i] = takeImplicitAddr(c, n.sons[0].sons[i])
+        elif n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv} and 
+             n.sons[0].sons[1].kind == nkPar:
+          var a = n.sons[0].sons[1]
+          a.sons[i] = takeImplicitAddr(c, a.sons[i])
+        else:
+          localError(n.sons[0].info, errXExpected, "tuple constructor")
+  else: nil
+  
+proc SemYield(c: PContext, n: PNode): PNode =
+  result = n
+  checkSonsLen(n, 1)
+  if c.p.owner == nil or c.p.owner.kind != skIterator:
+    LocalError(n.info, errYieldNotAllowedHere)
+  elif c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline:
+    LocalError(n.info, errYieldNotAllowedInTryStmt)
+  elif n.sons[0].kind != nkEmpty:
+    n.sons[0] = SemExprWithType(c, n.sons[0]) # check for type compatibility:
+    var restype = c.p.owner.typ.sons[0]
+    if restype != nil:
+      n.sons[0] = fitNode(c, restype, n.sons[0])
+      if n.sons[0].typ == nil: InternalError(n.info, "semYield")
+      SemYieldVarResult(c, n, restype)
+    else:
+      localError(n.info, errCannotReturnExpr)
+
 proc lookUpForDefined(c: PContext, i: PIdent, onlyCurrentScope: bool): PSym =
   if onlyCurrentScope: 
     result = SymtabLocalGet(c.tab, i)
@@ -1417,7 +1475,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     # because of the changed symbol binding, this does not mean that we
     # don't have to check the symbol for semantics here again!
     result = semSym(c, n, n.sym, flags)
-  of nkEmpty, nkNone: 
+  of nkEmpty, nkNone, nkCommentStmt: 
     nil
   of nkNilLit: 
     result.typ = getSysType(tyNil)
@@ -1505,10 +1563,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
       result = semIndirectOp(c, n, flags)
   of nkMacroStmt: 
     result = semMacroStmt(c, n, flags)
-  of nkWhenExpr:
-    result = semWhen(c, n, false)
-    result = semExpr(c, result)
-  of nkBracketExpr: 
+  of nkWhen:
+    if efWantStmt in flags:
+      result = semWhen(c, n, true)
+    else:
+      result = semWhen(c, n, false)
+      result = semExpr(c, result, flags)
+  of nkBracketExpr:
     checkMinSonsLen(n, 1)
     var s = qualifiedLookup(c, n.sons[0], {checkUndeclared})
     if s != nil and s.kind in {skProc, skMethod, skConverter, skIterator}: 
@@ -1562,6 +1623,45 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     result = n.sons[0]
   of nkStaticExpr:
     result = semStaticExpr(c, n)
+
+  of nkAsgn: result = semAsgn(c, n)
+  of nkBlockStmt: result = semBlock(c, n)
+  of nkStmtList: result = semStmtList(c, n)
+  of nkRaiseStmt: result = semRaise(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)
+  of nkDiscardStmt: result = semDiscard(c, n)
+  of nkWhileStmt: result = semWhile(c, n)
+  of nkTryStmt: result = semTry(c, n)
+  of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n)
+  of nkForStmt, nkParForStmt: result = semFor(c, n)
+  of nkCaseStmt: result = semCase(c, n)
+  of nkReturnStmt: result = semReturn(c, n)
+  of nkAsmStmt: result = semAsm(c, n)
+  of nkYieldStmt: result = semYield(c, n)
+  of nkPragma: pragma(c, c.p.owner, n, stmtPragmas)
+  of nkIteratorDef: result = semIterator(c, n)
+  of nkProcDef: result = semProc(c, n)
+  of nkMethodDef: result = semMethod(c, n)
+  of nkConverterDef: result = semConverterDef(c, n)
+  of nkMacroDef: result = semMacroDef(c, n)
+  of nkTemplateDef: result = semTemplateDef(c, n)
+  of nkImportStmt: 
+    if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "import")
+    result = evalImport(c, n)
+  of nkFromStmt: 
+    if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "from")
+    result = evalFrom(c, n)
+  of nkIncludeStmt: 
+    if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "include")
+    result = evalInclude(c, n)
+  of nkPragmaBlock:
+    result = semPragmaBlock(c, n)
+  of nkStaticStmt:
+    result = semStaticStmt(c, n)
   else:
     LocalError(n.info, errInvalidExpressionX,
                renderTree(n, {renderNoComments}))
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
new file mode 100644
index 000000000..139f8f15e
--- /dev/null
+++ b/compiler/sempass2.nim
@@ -0,0 +1,22 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+#  included from sem.nim
+
+# Second semantic checking pass over the AST. Necessary because the old
+# way had some inherent problems. Performs:
+# 
+# * procvar checks
+# * effect tracking
+# * closure analysis
+# * checks for invalid usages of compiletime magics
+# * checks for invalid usages of PNimNode
+# * later: will do an escape analysis for closures at least
+
+
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 193d6b7cf..666c57cd1 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -64,7 +64,7 @@ proc semDiscard(c: PContext, n: PNode): PNode =
   checkSonsLen(n, 1)
   if n.sons[0].kind != nkEmpty:
     n.sons[0] = semExprWithType(c, n.sons[0])
-    if n.sons[0].typ == nil: localError(n.info, errInvalidDiscard)
+    if isEmptyType(n.sons[0].typ): localError(n.info, errInvalidDiscard)
   
 proc semBreakOrContinue(c: PContext, n: PNode): PNode =
   result = n
@@ -162,60 +162,6 @@ proc semCase(c: PContext, n: PNode): PNode =
   if chckCovered and (covered != toCover(n.sons[0].typ)): 
     localError(n.info, errNotAllCasesCovered)
   closeScope(c.tab)
-
-proc SemReturn(c: PContext, n: PNode): PNode = 
-  result = n
-  checkSonsLen(n, 1)
-  if c.p.owner.kind notin {skConverter, skMethod, skProc, skMacro}:
-    LocalError(n.info, errXNotAllowedHere, "\'return\'")
-  elif n.sons[0].kind != nkEmpty:
-    # transform ``return expr`` to ``result = expr; return``
-    if c.p.resultSym != nil: 
-      var a = newNodeI(nkAsgn, n.sons[0].info)
-      addSon(a, newSymNode(c.p.resultSym))
-      addSon(a, n.sons[0])
-      n.sons[0] = semAsgn(c, a)
-      # optimize away ``result = result``:
-      if n[0][1].kind == nkSym and n[0][1].sym.kind == skResult: 
-        n.sons[0] = ast.emptyNode
-    else:
-      LocalError(n.info, errNoReturnTypeDeclared)
-
-proc SemYieldVarResult(c: PContext, n: PNode, restype: PType) =
-  var t = skipTypes(restype, {tyGenericInst})
-  case t.kind
-  of tyVar:
-    n.sons[0] = takeImplicitAddr(c, n.sons[0])
-  of tyTuple:
-    for i in 0.. <t.sonsLen:
-      var e = skipTypes(t.sons[i], {tyGenericInst})
-      if e.kind == tyVar:
-        if n.sons[0].kind == nkPar:
-          n.sons[0].sons[i] = takeImplicitAddr(c, n.sons[0].sons[i])
-        elif n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv} and 
-             n.sons[0].sons[1].kind == nkPar:
-          var a = n.sons[0].sons[1]
-          a.sons[i] = takeImplicitAddr(c, a.sons[i])
-        else:
-          localError(n.sons[0].info, errXExpected, "tuple constructor")
-  else: nil
-  
-proc SemYield(c: PContext, n: PNode): PNode =
-  result = n
-  checkSonsLen(n, 1)
-  if c.p.owner == nil or c.p.owner.kind != skIterator:
-    LocalError(n.info, errYieldNotAllowedHere)
-  elif c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline:
-    LocalError(n.info, errYieldNotAllowedInTryStmt)
-  elif n.sons[0].kind != nkEmpty:
-    n.sons[0] = SemExprWithType(c, n.sons[0]) # check for type compatibility:
-    var restype = c.p.owner.typ.sons[0]
-    if restype != nil:
-      n.sons[0] = fitNode(c, restype, n.sons[0])
-      if n.sons[0].typ == nil: InternalError(n.info, "semYield")
-      SemYieldVarResult(c, n, restype)
-    else:
-      localError(n.info, errCannotReturnExpr)
   
 proc fitRemoveHiddenConv(c: PContext, typ: Ptype, n: PNode): PNode = 
   result = fitNode(c, typ, n)
@@ -1125,111 +1071,53 @@ proc insertDestructors(c: PContext, varSection: PNode):
 
       return
 
-proc SemStmt(c: PContext, n: PNode): PNode = 
-  const                       # must be last statements in a block:
+proc semStmtList(c: PContext, n: PNode): PNode =
+  # these must be last statements in a block:
+  const
     LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
   result = n
-  if gCmd == cmdIdeTools: 
-    suggestStmt(c, n)
-  if nfSem in n.flags: return 
-  case n.kind
-  of nkAsgn: result = semAsgn(c, n)
-  of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkMacroStmt, nkCallStrLit: 
-    result = semCommand(c, n)
-  of nkEmpty, nkCommentStmt: nil
-  of nkNilLit:
-    # XXX too much work and fixing would break bootstrapping:
-    #Message(n.info, warnNilStatement)
-  of nkBlockStmt: result = semBlock(c, n)
-  of nkStmtList: 
-    var length = sonsLen(n)
-    for i in countup(0, length - 1):
-      case n.sons[i].kind
-      of nkFinally, nkExceptBranch:
-        # stand-alone finally and except blocks are
-        # transformed into regular try blocks:
-        #
-        # var f = fopen("somefile") | var f = fopen("somefile")
-        # finally: fclose(f)        | try:
-        # ...                       |   ...
-        #                           | finally:
-        #                           |   fclose(f)
-        var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
-        var body = newNodeI(nkStmtList, n.sons[i].info)
-        if i < n.sonsLen - 1:
-          body.sons = n.sons[(i+1)..(-1)]
-        tryStmt.addSon(body)
-        tryStmt.addSon(n.sons[i])
-        n.sons[i] = semTry(c, tryStmt)
-        n.sons.setLen(i+1)
-        return
-      else:
-        n.sons[i] = semStmt(c, n.sons[i])
-        case n.sons[i].kind
-        of nkVarSection, nkLetSection:
-          let (outer, inner) = insertDestructors(c, n.sons[i])
-          if outer != nil:
-            n.sons[i] = outer
-            for j in countup(i+1, length-1):
-              inner.addSon(SemStmt(c, n.sons[j]))
-            n.sons.setLen(i+1)
-            return
-        of LastBlockStmts: 
-          for j in countup(i + 1, length - 1): 
-            case n.sons[j].kind
-            of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil
-            else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
-        else: nil
-  of nkRaiseStmt: result = semRaise(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)
-  of nkWhenStmt: result = semWhen(c, n)
-  of nkDiscardStmt: result = semDiscard(c, n)
-  of nkWhileStmt: result = semWhile(c, n)
-  of nkTryStmt: result = semTry(c, n)
-  of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n)
-  of nkForStmt, nkParForStmt: result = semFor(c, n)
-  of nkCaseStmt: result = semCase(c, n)
-  of nkReturnStmt: result = semReturn(c, n)
-  of nkAsmStmt: result = semAsm(c, n)
-  of nkYieldStmt: result = semYield(c, n)
-  of nkPragma: pragma(c, c.p.owner, n, stmtPragmas)
-  of nkIteratorDef: result = semIterator(c, n)
-  of nkProcDef: result = semProc(c, n)
-  of nkMethodDef: result = semMethod(c, n)
-  of nkConverterDef: result = semConverterDef(c, n)
-  of nkMacroDef: result = semMacroDef(c, n)
-  of nkTemplateDef: result = semTemplateDef(c, n)
-  of nkImportStmt: 
-    if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "import")
-    result = evalImport(c, n)
-  of nkFromStmt: 
-    if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "from")
-    result = evalFrom(c, n)
-  of nkIncludeStmt: 
-    if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "include")
-    result = evalInclude(c, n)
-  of nkPragmaBlock:
-    result = semPragmaBlock(c, n)
-  of nkStaticStmt:
-    result = semStaticStmt(c, n)
-  else:
-    # in interactive mode, we embed the expression in an 'echo':
-    if gCmd == cmdInteractive:
-      result = buildEchoStmt(c, semExpr(c, n))
+  var length = sonsLen(n)
+  for i in countup(0, length - 1):
+    case n.sons[i].kind
+    of nkFinally, nkExceptBranch:
+      # stand-alone finally and except blocks are
+      # transformed into regular try blocks:
+      #
+      # var f = fopen("somefile") | var f = fopen("somefile")
+      # finally: fclose(f)        | try:
+      # ...                       |   ...
+      #                           | finally:
+      #                           |   fclose(f)
+      var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
+      var body = newNodeI(nkStmtList, n.sons[i].info)
+      if i < n.sonsLen - 1:
+        body.sons = n.sons[(i+1)..(-1)]
+      tryStmt.addSon(body)
+      tryStmt.addSon(n.sons[i])
+      n.sons[i] = semTry(c, tryStmt)
+      n.sons.setLen(i+1)
+      return
     else:
-      result = semExprNoType(c, n)
-      #LocalError(n.info, errStmtExpected)
-      #result = ast.emptyNode
-  if result == nil: 
-    InternalError(n.info, "SemStmt: result = nil")
-    # error correction:
-    result = emptyNode
-  else:
-    incl(result.flags, nfSem)
+      n.sons[i] = semStmt(c, n.sons[i])
+      case n.sons[i].kind
+      of nkVarSection, nkLetSection:
+        let (outer, inner) = insertDestructors(c, n.sons[i])
+        if outer != nil:
+          n.sons[i] = outer
+          for j in countup(i+1, length-1):
+            inner.addSon(SemStmt(c, n.sons[j]))
+          n.sons.setLen(i+1)
+          return
+      of LastBlockStmts: 
+        for j in countup(i + 1, length - 1): 
+          case n.sons[j].kind
+          of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil
+          else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
+      else: nil
+
+proc SemStmt(c: PContext, n: PNode): PNode = 
+  # now: simply an alias:
+  result = semExprNoType(c, n)
 
 proc semStmtScope(c: PContext, n: PNode): PNode =
   openScope(c.tab)