summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorZahary Karadjov <zahary@gmail.com>2012-04-13 16:01:26 +0300
committerZahary Karadjov <zahary@gmail.com>2012-04-13 21:03:49 +0300
commitf25c638dc4109445ce1476d6e6f18be034805a0a (patch)
tree9ce68a56248a03ca6a97a4a80b1cf376f66ec531 /compiler
parentcaf78780096c157560f50cf0a4958fdf07d09a7d (diff)
downloadNim-f25c638dc4109445ce1476d6e6f18be034805a0a.tar.gz
experimental support for preserving local variable names in the generated code
Diffstat (limited to 'compiler')
-rwxr-xr-xcompiler/ast.nim28
-rwxr-xr-xcompiler/ccgtypes.nim70
-rwxr-xr-xcompiler/options.nim4
-rwxr-xr-xcompiler/semgnrc.nim2
-rwxr-xr-xcompiler/semstmts.nim12
-rwxr-xr-xcompiler/wordrecg.nim52
6 files changed, 146 insertions, 22 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index c6a2b32e7..10a3d8039 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -244,12 +244,17 @@ const
   sfDispatcher* = sfDeadCodeElim # copied method symbol is the dispatcher
   sfNoInit* = sfMainModule       # don't generate code to init the variable
 
-  sfImmediate* = sfDeadCodeElim  # macro or template is immediately expanded
-                                 # without considering any possible overloads
+  sfImmediate* = sfDeadCodeElim
+    # macro or template is immediately expanded
+    # without considering any possible overloads
   
-  sfAnon* = sfCompilerProc  # symbol name that was generated by the compiler
-                            # the compiler will avoid printing such names 
-                            # in user messages.
+  sfAnon* = sfDiscardable
+    # symbol name that was generated by the compiler
+    # the compiler will avoid printing such names 
+    # in user messages.
+  
+  sfShadowed* = sfInnerProc
+    # a variable that was shadowed in some inner scope
 
 const
   # getting ready for the future expr/stmt merge
@@ -647,6 +652,14 @@ const
   resultPos* = 5
   dispatcherPos* = 6 # caution: if method has no 'result' it can be position 5!
 
+  nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
+                  nkCommand, nkCallStrLit}
+
+  nkLambdaKinds* = {nkLambda, nkDo}
+
+  skLocalVars* = {skVar, skLet, skForVar, skParam}
+
+
 # creator procs:
 proc NewSym*(symKind: TSymKind, Name: PIdent, owner: PSym): PSym
 proc NewType*(kind: TTypeKind, owner: PSym): PType
@@ -691,11 +704,6 @@ proc copyNode*(src: PNode): PNode
 proc copyTree*(src: PNode): PNode
   # does copy its sons!
 
-const nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand,
-                      nkCallStrLit}
-
-const nkLambdaKinds* = {nkLambda, nkDo}
-
 proc isCallExpr*(n: PNode): bool =
   result = n.kind in nkCallKinds
 
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 6195ff2f4..3578b1f5e 100755
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -32,6 +32,15 @@ proc mangle(name: string): string =
       add(result, "HEX")
       add(result, toHex(ord(name[i]), 2))
 
+proc isCKeyword(w: PIdent): bool =
+  # nimrod and C++ share some keywords
+  # it's more efficient to test the whole nimrod keywords range
+  case w.id
+  of cppKeywordsLow..cppKeywordsHigh,
+     nimKeywordsLow..nimKeywordsHigh,
+     ord(wInline): return true
+  else: return false
+
 proc mangleName(s: PSym): PRope = 
   result = s.loc.r
   if result == nil: 
@@ -45,9 +54,64 @@ proc mangleName(s: PSym): PRope =
       of skTemp, skParam, skType, skEnumField, skModule: 
         result = toRope("%")
       else: InternalError(s.info, "mangleName")
-    app(result, toRope(mangle(s.name.s)))
-    app(result, "_")
-    app(result, toRope(s.id))
+    when oKeepVariableNames:
+      let keepOrigName = s.kind in skLocalVars - {skForVar} and 
+        {sfFromGeneric, sfGlobal, sfShadowed} * s.flags == {} and
+        not isCKeyword(s.name)
+      # XXX: This is still very experimental
+      #
+      # Even with all these inefficient checks, the bootstrap
+      # time is actually improved. This is probably because so many
+      # rope concatenations and are now eliminated.
+      #
+      # Future notes:
+      # sfFromGeneric seems to be needed in order to avoid multiple
+      # definitions of certain varialbes generated in transf with
+      # names such as:
+      # `r`, `res`
+      # I need to study where these come from.
+      #
+      # about sfShadowed:
+      # consider the following nimrod code:
+      #   var x = 10
+      #   block:
+      #     var x = something(x)
+      # The generated C code will be:
+      #   NI x;
+      #   x = 10;
+      #   {
+      #     NI x;
+      #     x = something(x); // Oops, x is already shadowed here
+      #   }
+      # Right now, we work-around by not keeping the original name
+      # of the shadowed variable, but we can do better - we can
+      # create an alternative reference to it in the outer scope and
+      # use that in the inner scope.
+      #
+      # about isCKeyword:
+      # nimrod variable names can be C keywords.
+      # We need to avoid such names in the generated code.
+      # XXX: Study whether mangleName is called just once per variable.
+      # Otherwise, there might be better place to do this.
+      #
+      # about sfGlobal:
+      # This seems to be harder - a top level extern variable from
+      # another modules can have the same name as a local one.
+      # Maybe we should just implement sfShadowed for them too.
+      #
+      # about skForVar:
+      # These are not properly scoped now - we need to add blocks
+      # around for loops in transf
+      if keepOrigName:
+        result = s.name.s.toRope
+      else:
+        app(result, toRope(mangle(s.name.s)))
+        app(result, "_")
+        app(result, toRope(s.id))
+    else:
+      app(result, toRope(mangle(s.name.s)))
+      app(result, "_")
+      app(result, toRope(s.id))
     s.loc.r = result
 
 proc isCompileTimeOnly(t: PType): bool =
diff --git a/compiler/options.nim b/compiler/options.nim
index 0d7763be0..3a2352c7f 100755
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -113,7 +113,9 @@ var
   gKeepComments*: bool = true # whether the parser needs to keep comments
   implicitImports*: seq[string] = @[] # modules that are to be implicitly imported
   implicitIncludes*: seq[string] = @[] # modules that are to be implicitly included
-  
+
+const oKeepVariableNames* = true
+
 proc mainCommandArg*: string =
   ## This is intended for commands like check or parse
   ## which will work on the main project file unless
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index 6a1c2f529..d8f017c4c 100755
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -15,6 +15,8 @@
 # So we have to eval templates/macros right here so that symbol
 # lookup can be accurate.
 
+# included from sem.nim
+
 type 
   TSemGenericFlag = enum 
     withinBind, withinTypeDesc
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 0449c5644..d729a691f 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -216,7 +216,14 @@ proc fitRemoveHiddenConv(c: PContext, typ: Ptype, n: PNode): PNode =
     result = result.sons[1]
   elif not sameType(result.typ, typ): 
     changeType(result, typ)
-  
+
+proc findShadowedVar(c: PContext, v: PSym): PSym =
+  for i in countdown(c.tab.tos - 2, 0):
+    let shadowed = StrTableGet(c.tab.stack[i], v.name)
+    if shadowed != nil:
+      if shadowed.kind in skLocalVars:
+        return shadowed
+
 proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
   if isTopLevel(c): 
     result = semIdentWithPragma(c, kind, n, {sfExported})
@@ -267,6 +274,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
     for j in countup(0, length-3):
       var v = semIdentDef(c, a.sons[j], symkind)
       addInterfaceDecl(c, v)
+      when oKeepVariableNames:
+        let shadowed = findShadowedVar(c, v)
+        if shadowed != nil: shadowed.flags.incl(sfShadowed)
       if def != nil and def.kind != nkEmpty:
         # this is only needed for the evaluation pass:
         v.ast = def
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index f96ba7751..3ae9f7be9 100755
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -38,8 +38,8 @@ type
     wMagic, wThread, wFinal, wProfiler, wObjChecks,
     wImmediate, wImportCpp, wImportObjC,
     wImportCompilerProc,
-    wImportc, wExportc, wExtern, wIncompleteStruct,
-    wAlign, wNodecl, wPure, wVolatile, wRegister, wSideeffect, wHeader, 
+    wImportc, wExportc, wIncompleteStruct,
+    wAlign, wNodecl, wPure, wSideeffect, wHeader,
     wNosideeffect, wNoreturn, wMerge, wLib, wDynlib, wCompilerproc, wProcVar, 
     wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef, 
     wLinedir, wStacktrace, wLinetrace, wLink, wCompile, 
@@ -58,13 +58,36 @@ type
     wWatchPoint, wSubsChar, 
     wAcyclic, wShallow, wUnroll, wLinearScanEnd,
     wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar, wEmit, wNoStackFrame,
-    wImplicitStatic, wGlobal
+    wImplicitStatic, wGlobal,
+
+    wAuto, wBool, wCatch, wChar, wClass,
+    wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
+    wExplicit, wExtern, wFalse, wFloat, wFriend,
+    wGoto, wInt, wLong, wMutable, wNamespace, wNew, wOperator,
+    wPrivate, wProtected, wPublic, wRegister, wReinterpret_cast,
+    wShort, wSigned, wSizeof, wStatic_cast, wStruct, wSwitch,
+    wThis, wThrow, wTrue, wTypedef, wTypeid, wTypename,
+    wUnion, wUnsigned, wUsing, wVirtual, wVoid, wVolatile, wWchar_t,
+
+    wAlignas, wAlignof, wConstexpr, wDecltype, wNullptr, wNoexcept,
+    wThread_local, wStatic_assert, wChar16_t, wChar32_t,
     
   TSpecialWords* = set[TSpecialWord]
 
 const 
   oprLow* = ord(wColon)
   oprHigh* = ord(wDotDot)
+  
+  nimKeywordsLow* = ord(wAsm)
+  nimKeywordsHigh* = ord(wYield)
+  
+  cppKeywordsLow* = ord(wAuto)
+  cppKeywordsHigh* = ord(wChar32_t)
+  
+  cppNimSharedKeywords* = {
+    wAsm, wBreak, wCase, wConst, wContinue, wDo, wElse, wEnum, wExport,
+    wFor, wIf, wReturn, wStatic, wTemplate, wTry, wWhile }
+
   specialWords*: array[low(TSpecialWord)..high(TSpecialWord), string] = ["", 
     
     "addr", "and", "as", "asm", "atomic", 
@@ -86,14 +109,14 @@ const
     "magic", "thread", "final", "profiler", "objchecks", 
     
     "immediate", "importcpp", "importobjc",
-    "importcompilerproc", "importc", "exportc", "extern", "incompletestruct",
-    "align", "nodecl", "pure", "volatile", "register", "sideeffect", 
+    "importcompilerproc", "importc", "exportc", "incompletestruct",
+    "align", "nodecl", "pure", "sideeffect",
     "header", "nosideeffect", "noreturn", "merge", "lib", "dynlib", 
     "compilerproc", "procvar", "fatal", "error", "warning", "hint", "line", 
     "push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace", 
     "link", "compile", "linksys", "deprecated", "varargs", 
     "byref", "callconv", "breakpoint", "debugger", "nimcall", "stdcall", 
-    "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "closure", 
+    "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "closure",
     "noconv", "on", "off", "checks", "rangechecks", "boundchecks", 
     "overflowchecks", "nilchecks",
     "floatchecks", "nanchecks", "infchecks",
@@ -107,7 +130,22 @@ const
     "watchpoint",
     "subschar", "acyclic", "shallow", "unroll", "linearscanend",
     "write", "putenv", "prependenv", "appendenv", "threadvar", "emit",
-    "nostackframe", "implicitstatic", "global"]
+    "nostackframe", "implicitstatic", "global",
+    
+    "auto", "bool", "catch", "char", "class",
+    "const_cast", "default", "delete", "double",
+    "dynamic_cast", "explicit", "extern", "false",
+    "float", "friend", "goto", "int", "long", "mutable",
+    "namespace", "new", "operator",
+    "private", "protected", "public", "register", "reinterpret_cast",
+    "short", "signed", "sizeof", "static_cast", "struct", "switch",
+    "this", "throw", "true", "typedef", "typeid",
+    "typename", "union", "unsigned", "using", "virtual", "void", "volatile",
+    "wchar_t",
+
+    "alignas", "alignof", "constexpr", "decltype", "nullptr", "noexcept",
+    "thread_local", "static_assert", "char16_t", "char32_t",
+    ]
 
 proc findStr*(a: openarray[string], s: string): int = 
   for i in countup(low(a), high(a)):