summary refs log tree commit diff stats
path: root/compiler/ast.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/ast.nim')
-rw-r--r--compiler/ast.nim638
1 files changed, 304 insertions, 334 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 883b68d71..d94f09ccb 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -9,12 +9,12 @@
 
 # abstract syntax tree + symbol table
 
-import 
-  msgs, hashes, nversion, options, strutils, crc, ropes, idents, lists, 
+import
+  msgs, hashes, nversion, options, strutils, crc, ropes, idents, lists,
   intsets, idgen
 
 type
-  TCallingConvention* = enum 
+  TCallingConvention* = enum
     ccDefault,                # proc has no explicit calling convention
     ccStdCall,                # procedure is stdcall
     ccCDecl,                  # cdecl
@@ -26,12 +26,12 @@ type
     ccClosure,                # proc has a closure
     ccNoConvention            # needed for generating proper C procs sometimes
 
-const 
-  CallingConvToStr*: array[TCallingConvention, string] = ["", "stdcall", 
+const
+  CallingConvToStr*: array[TCallingConvention, string] = ["", "stdcall",
     "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall",
     "closure", "noconv"]
 
-type 
+type
   TNodeKind* = enum # order is extremely important, because ranges are used
                     # to check whether a node belongs to a certain class
     nkNone,               # unknown node kind: indicates an error
@@ -64,13 +64,13 @@ type
                           # end of atoms
     nkMetaNode_Obsolete,  # difficult to explain; represents itself
                           # (used for macros)
-    nkDotCall,            # used to temporarily flag a nkCall node; 
+    nkDotCall,            # used to temporarily flag a nkCall node;
                           # this is used
                           # for transforming ``s.len`` to ``len(s)``
 
     nkCommand,            # a call like ``p 2, 4`` without parenthesis
     nkCall,               # a call like p(x, y) or an operation like +(a, b)
-    nkCallStrLit,         # a call with a string literal 
+    nkCallStrLit,         # a call with a string literal
                           # x"abc" has two sons: nkIdent, nkRStrLit
                           # x"""abc""" has two sons: nkIdent, nkTripleStrLit
     nkInfix,              # a call like (a + b)
@@ -126,7 +126,7 @@ type
 
     nkAsgn,               # a = b
     nkFastAsgn,           # internal node for a fast ``a = b``
-                          # (no string copy) 
+                          # (no string copy)
     nkGenericParams,      # generic parameters
     nkFormalParams,       # formal parameters
     nkOfInherit,          # inherited from symbol
@@ -192,10 +192,11 @@ type
 
     nkWith,               # distinct with `foo`
     nkWithout,            # distinct without `foo`
-    
+
     nkTypeOfExpr,         # type(1+2)
     nkObjectTy,           # object body
     nkTupleTy,            # tuple body
+    nkTupleClassTy,       # tuple type class
     nkTypeClassTy,        # user-defined type class
     nkStaticTy,           # ``static[T]``
     nkRecList,            # list of object parts
@@ -226,7 +227,7 @@ type
   TSymFlag* = enum    # already 32 flags!
     sfUsed,           # read access of sym (for warnings) or simply used
     sfExported,       # symbol is exported from module
-    sfFromGeneric,    # symbol is instantiation of a generic; this is needed 
+    sfFromGeneric,    # symbol is instantiation of a generic; this is needed
                       # for symbol file generation; such symbols should always
                       # be written into the ROD file
     sfGlobal,         # symbol is at global scope
@@ -256,7 +257,7 @@ type
     sfThread,         # proc will run as a thread
                       # variable is a thread variable
     sfCompileTime,    # proc can be evaluated at compile time
-    sfMerge,          # proc can be merged with itself
+    sfConstructor,    # proc is a C++ constructor
     sfDeadCodeElim,   # dead code elimination for the module is turned on
     sfBorrow,         # proc is borrowed
     sfInfixCall,      # symbol needs infix call syntax in target language;
@@ -284,9 +285,9 @@ const
 
   sfAnon* = sfDiscardable
     # symbol name that was generated by the compiler
-    # the compiler will avoid printing such names 
+    # the compiler will avoid printing such names
     # in user messages.
-      
+
   sfNoForward* = sfRegister
     # forward declarations are not required (per module)
 
@@ -300,7 +301,7 @@ const
   # getting ready for the future expr/stmt merge
   nkWhen* = nkWhenStmt
   nkWhenExpr* = nkWhenStmt
-  nkEffectList* = nkArgList 
+  nkEffectList* = nkArgList
   # hacks ahead: an nkEffectList is a node with 4 children:
   exceptionEffects* = 0 # exceptions at position 0
   usesEffects* = 1      # read effects at position 1
@@ -314,14 +315,14 @@ type
                      # XXX put this into an include file to avoid this issue!
     tyNone, tyBool, tyChar,
     tyEmpty, tyArrayConstr, tyNil, tyExpr, tyStmt, tyTypeDesc,
-    tyGenericInvokation, # ``T[a, b]`` for types to invoke
+    tyGenericInvocation, # ``T[a, b]`` for types to invoke
     tyGenericBody,       # ``T[a, b, body]`` last parameter is the body
     tyGenericInst,       # ``T[a, b, realInstance]`` instantiated generic type
                          # realInstance will be a concrete type like tyObject
                          # unless this is an instance of a generic alias type.
                          # then realInstance will be the tyGenericInst of the
                          # completely (recursively) resolved alias.
-                         
+
     tyGenericParam,      # ``a`` in the above patterns
     tyDistinct,
     tyEnum,
@@ -340,14 +341,14 @@ type
     tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers
     tyFloat, tyFloat32, tyFloat64, tyFloat128,
     tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64,
-    tyBigNum, 
-    tyConst, tyMutable, tyVarargs, 
+    tyBigNum,
+    tyConst, tyMutable, tyVarargs,
     tyIter, # unused
     tyProxy # used as errornous type (for idetools)
-    
+
     tyBuiltInTypeClass #\
       # Type such as the catch-all object, tuple, seq, etc
-    
+
     tyUserTypeClass #\
       # the body of a user-defined type class
 
@@ -357,27 +358,27 @@ type
       # tyGenericInst represents concrete types, while
       # this is still a "generic param" that will bind types
       # and resolves them during sigmatch and instantiation.
-    
+
     tyCompositeTypeClass #\
       # Type such as seq[Number]
-      # The notes for tyUserTypeClassInst apply here as well 
+      # The notes for tyUserTypeClassInst apply here as well
       # sons[0]: the original expression used by the user.
       # sons[1]: fully expanded and instantiated meta type
       # (potentially following aliases)
-    
+
     tyAnd, tyOr, tyNot #\
       # boolean type classes such as `string|int`,`not seq`,
       # `Sortable and Enumable`, etc
-    
+
     tyAnything #\
       # a type class matching any type
-    
+
     tyStatic #\
       # a value known at compile type (the underlying type is .base)
-    
+
     tyFromExpr #\
       # This is a type representing an expression that depends
-      # on generic parameters (the exprsesion is stored in t.n)
+      # on generic parameters (the expression is stored in t.n)
       # It will be converted to a real type only during generic
       # instantiation and prior to this it has the potential to
       # be any type.
@@ -390,7 +391,7 @@ type
       # sons[0]: type of containing object or tuple
       # sons[1]: field type
       # .n: nkDotExpr storing the field name
-    
+
 static:
   # remind us when TTypeKind stops to fit in a single 64-bit word
   assert TTypeKind.high.ord <= 63
@@ -408,7 +409,7 @@ const
                     tyAnd, tyOr, tyNot, tyAnything}
 
   tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyExpr} + tyTypeClasses
- 
+
 type
   TTypeKinds* = set[TTypeKind]
 
@@ -428,7 +429,8 @@ type
     nfExplicitCall # x.y() was used instead of x.y
     nfExprCall  # this is an attempt to call a regular expression
     nfIsRef     # this node is a 'ref' node; used for the VM
- 
+    nfIsCursor  # this node is attached a cursor; used for idetools
+
   TNodeFlags* = set[TNodeFlag]
   TTypeFlag* = enum   # keep below 32 for efficiency reasons (now: 28)
     tfVarargs,        # procedure has C styled varargs
@@ -447,7 +449,7 @@ type
                       # proc foo(L: static[int]): array[L, int]
                       # can be attached to ranges to indicate that the range
                       # depends on unresolved static params.
-    tfRetType,        # marks return types in proc (used to detect type classes 
+    tfRetType,        # marks return types in proc (used to detect type classes
                       # used as return types for return type inference)
     tfCapturesEnv,    # whether proc really captures some environment
     tfByCopy,         # pass object/tuple by copy (C backend)
@@ -455,10 +457,10 @@ type
     tfIterator,       # type is really an iterator, not a tyProc
     tfShared,         # type is 'shared'
     tfNotNil,         # type cannot be 'nil'
-    
+
     tfNeedsInit,      # type constains a "not nil" constraint somewhere or some
-                      # other type so that it requires inititalization
-    tfHasShared,      # type constains a "shared" constraint modifier somewhere
+                      # other type so that it requires initalization
+    tfVarIsPtr,       # 'var' type is translated like 'ptr' even in C++ mode
     tfHasMeta,        # type contains "wildcard" sub-types such as generic params
                       # or other type classes
     tfHasGCedMem,     # type contains GC'ed memory
@@ -519,41 +521,42 @@ const
   tfGcSafe* = tfThread
   tfObjHasKids* = tfEnumHasHoles
   skError* = skUnknown
-  
+
   # type flags that are essential for type equality:
-  eqTypeFlags* = {tfIterator, tfShared, tfNotNil}
+  eqTypeFlags* = {tfIterator, tfShared, tfNotNil, tfVarIsPtr}
 
 type
   TMagic* = enum # symbols that require compiler magic:
     mNone,
     mDefined, mDefinedInScope, mCompiles,
-    mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf,
+    mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf,
     mEcho, mShallowCopy, mSlurp, mStaticExec,
     mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
-    mUnaryLt, mSucc, 
-    mPred, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray, 
-    mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref, 
-    mGCunref, mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, 
-    mDivI64, mModI64,
+    mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mLengthOpenArray,
+    mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, mGCref,
+    mGCunref,
+
+    mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
+    mDivI64, mModI64, mSucc, mPred,
     mAddF64, mSubF64, mMulF64, mDivF64,
-    mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, 
+
+    mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI,
     mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64,
-    mMinF64, mMaxF64, mAddU, mSubU, mMulU, 
+    mMinF64, mMaxF64, mAddU, mSubU, mMulU,
     mDivU, mModU, mEqI, mLeI,
-    mLtI, 
-    mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64, 
-    mLeU, mLtU, mLeU64, mLtU64, 
-    mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, 
+    mLtI,
+    mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64,
+    mLeU, mLtU, mLeU64, mLtU64,
+    mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
     mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mEqProc, mUnaryMinusI,
-    mUnaryMinusI64, mAbsI, mAbsI64, mNot, 
-    mUnaryPlusI, mBitnotI, mUnaryPlusI64, 
-    mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, 
-    mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, 
-    mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, 
-    mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, 
-    mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, 
-    mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT, 
-    mConTArr, mConTT, mSlice,
+    mUnaryMinusI64, mAbsI, mAbsI64, mNot,
+    mUnaryPlusI, mBitnotI, mUnaryPlusI64,
+    mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
+    mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
+    mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
+    mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr,
+    mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet,
+    mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mSlice,
     mFields, mFieldPairs, mOmpParFor,
     mAppendStrCh, mAppendStrStr, mAppendSeqElem,
     mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq,
@@ -583,36 +586,35 @@ type
 
 # things that we can evaluate safely at compile time, even if not asked for it:
 const
-  ctfeWhitelist* = {mNone, mUnaryLt, mSucc, 
-    mPred, mInc, mDec, mOrd, mLengthOpenArray, 
-    mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr, 
-    mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64, 
+  ctfeWhitelist* = {mNone, mUnaryLt, mSucc,
+    mPred, mInc, mDec, mOrd, mLengthOpenArray,
+    mLengthStr, mLengthArray, mLengthSeq, mIncl, mExcl, mCard, mChr,
+    mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
     mDivI64, mModI64, mAddF64, mSubF64, mMulF64, mDivF64,
-    mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, 
+    mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI,
     mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64, mMinI64, mMaxI64,
-    mMinF64, mMaxF64, mAddU, mSubU, mMulU, 
+    mMinF64, mMaxF64, mAddU, mSubU, mMulU,
     mDivU, mModU, mEqI, mLeI,
-    mLtI, 
-    mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64, 
-    mLeU, mLtU, mLeU64, mLtU64, 
-    mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef, 
-    mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI, 
-    mUnaryMinusI64, mAbsI, mAbsI64, mNot, 
-    mUnaryPlusI, mBitnotI, mUnaryPlusI64, 
-    mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64, 
-    mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, 
-    mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, 
-    mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, 
-    mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, 
-    mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT, 
-    mConTArr, mConTT,
-    mAppendStrCh, mAppendStrStr, mAppendSeqElem, 
+    mLtI,
+    mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64,
+    mLeU, mLtU, mLeU64, mLtU64,
+    mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
+    mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI,
+    mUnaryMinusI64, mAbsI, mAbsI64, mNot,
+    mUnaryPlusI, mBitnotI, mUnaryPlusI64,
+    mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
+    mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
+    mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
+    mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr,
+    mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet,
+    mPlusSet, mMinusSet, mSymDiffSet, mConStrStr,
+    mAppendStrCh, mAppendStrStr, mAppendSeqElem,
     mInRange, mInSet, mRepr,
     mCopyStr, mCopyStrLast}
   # magics that require special semantic checking and
   # thus cannot be overloaded (also documented in the spec!):
   SpecialSemMagics* = {
-    mDefined, mDefinedInScope, mCompiles, mLow, mHigh, mSizeOf, mIs, mOf, 
+    mDefined, mDefinedInScope, mCompiles, mLow, mHigh, mSizeOf, mIs, mOf,
     mEcho, mShallowCopy, mExpandToAst, mParallel, mSpawn, mAstToStr}
 
 type
@@ -633,49 +635,51 @@ type
       floatVal*: BiggestFloat
     of nkStrLit..nkTripleStrLit:
       strVal*: string
-    of nkSym: 
+    of nkSym:
       sym*: PSym
-    of nkIdent: 
+    of nkIdent:
       ident*: PIdent
     else:
       sons*: TNodeSeq
     comment*: string
-  
+
   TSymSeq* = seq[PSym]
   TStrTable* = object         # a table[PIdent] of PSym
     counter*: int
     data*: TSymSeq
-  
+
   # -------------- backend information -------------------------------
-  TLocKind* = enum 
+  TLocKind* = enum
     locNone,                  # no location
     locTemp,                  # temporary location
     locLocalVar,              # location is a local variable
     locGlobalVar,             # location is a global variable
     locParam,                 # location is a parameter
     locField,                 # location is a record field
-    locArrayElem,             # location is an array element
     locExpr,                  # "location" is really an expression
     locProc,                  # location is a proc (an address of a procedure)
     locData,                  # location is a constant
     locCall,                  # location is a call expression
     locOther                  # location is something other
-  TLocFlag* = enum 
+  TLocFlag* = enum
     lfIndirect,               # backend introduced a pointer
-    lfParamCopy,              # backend introduced a parameter copy (LLVM)
+    lfFullExternalName, # only used when 'gCmd == cmdPretty': Indicates
+      # that the symbol has been imported via 'importc: "fullname"' and
+      # no format string.
     lfNoDeepCopy,             # no need for a deep copy
     lfNoDecl,                 # do not declare it in C
     lfDynamicLib,             # link symbol to dynamic library
     lfExportLib,              # export symbol for dynamic library generation
     lfHeader,                 # include header file for symbol
-    lfImportCompilerProc      # ``importc`` of a compilerproc
-  TStorageLoc* = enum 
+    lfImportCompilerProc,     # ``importc`` of a compilerproc
+    lfSingleUse               # no location yet and will only be used once
+  TStorageLoc* = enum
     OnUnknown,                # location is unknown (stack, heap or static)
     OnStack,                  # location is on hardware stack
     OnHeap                    # location is on heap or global
                               # (reference counting needed)
   TLocFlags* = set[TLocFlag]
-  TLoc*{.final.} = object     
+  TLoc* = object
     k*: TLocKind              # kind of location
     s*: TStorageLoc
     flags*: TLocFlags         # location's flags
@@ -687,7 +691,7 @@ type
 
   # ---------------- end of backend information ------------------------------
 
-  TLibKind* = enum 
+  TLibKind* = enum
     libHeader, libDynamic
   TLib* = object of lists.TListEntry # also misused for headers!
     kind*: TLibKind
@@ -695,17 +699,17 @@ type
     isOverriden*: bool
     name*: PRope
     path*: PNode              # can be a string literal!
-  
+
   TInstantiation* = object
     sym*: PSym
     concreteTypes*: seq[PType]
     usedBy*: seq[int32]       # list of modules using the generic
                               # needed in caas mode for purging the cache
-                              # XXX: it's possible to switch to a 
+                              # XXX: it's possible to switch to a
                               # simple ref count here
-  
+
   PInstantiation* = ref TInstantiation
- 
+
   TScope* = object
     depthLevel*: int
     symbols*: TStrTable
@@ -772,7 +776,7 @@ type
     constraint*: PNode        # additional constraints like 'lit|result'; also
                               # misused for the codegenDecl pragma in the hope
                               # it won't cause problems
-  
+
   TTypeSeq* = seq[PType]
   TLockLevel* = distinct int16
   TType* {.acyclic.} = object of TIdObj # \
@@ -805,45 +809,45 @@ type
     lockLevel*: TLockLevel    # lock level as required for deadlock checking
     loc*: TLoc
 
-  TPair*{.final.} = object 
+  TPair* = object
     key*, val*: RootRef
 
   TPairSeq* = seq[TPair]
-  TTable*{.final.} = object   # the same as table[PObject] of PObject
+  TTable* = object   # the same as table[PObject] of PObject
     counter*: int
     data*: TPairSeq
 
-  TIdPair*{.final.} = object 
+  TIdPair* = object
     key*: PIdObj
     val*: RootRef
 
   TIdPairSeq* = seq[TIdPair]
-  TIdTable*{.final.} = object # the same as table[PIdent] of PObject
+  TIdTable* = object # the same as table[PIdent] of PObject
     counter*: int
     data*: TIdPairSeq
 
-  TIdNodePair*{.final.} = object 
+  TIdNodePair* = object
     key*: PIdObj
     val*: PNode
 
   TIdNodePairSeq* = seq[TIdNodePair]
-  TIdNodeTable*{.final.} = object # the same as table[PIdObj] of PNode
+  TIdNodeTable* = object # the same as table[PIdObj] of PNode
     counter*: int
     data*: TIdNodePairSeq
 
-  TNodePair*{.final.} = object 
+  TNodePair* = object
     h*: THash                 # because it is expensive to compute!
     key*: PNode
     val*: int
 
   TNodePairSeq* = seq[TNodePair]
-  TNodeTable*{.final.} = object # the same as table[PNode] of int;
+  TNodeTable* = object # the same as table[PNode] of int;
                                 # nodes are compared by structure!
     counter*: int
     data*: TNodePairSeq
 
   TObjectSeq* = seq[RootRef]
-  TObjectSet*{.final.} = object 
+  TObjectSet* = object
     counter*: int
     data*: TObjectSeq
 
@@ -854,27 +858,27 @@ type
 # same name as an imported module. This is necessary because of
 # the poor naming choices in the standard library.
 
-const 
+const
   OverloadableSyms* = {skProc, skMethod, skIterator, skClosureIterator,
     skConverter, skModule, skTemplate, skMacro}
 
-  GenericTypes*: TTypeKinds = {tyGenericInvokation, tyGenericBody, 
+  GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody,
     tyGenericParam}
-  
-  StructuralEquivTypes*: TTypeKinds = {tyArrayConstr, tyNil, tyTuple, tyArray, 
+
+  StructuralEquivTypes*: TTypeKinds = {tyArrayConstr, tyNil, tyTuple, tyArray,
     tySet, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc, tyOpenArray,
     tyVarargs}
-  
+
   ConcreteTypes*: TTypeKinds = { # types of the expr that may occur in::
                                  # var x = expr
-    tyBool, tyChar, tyEnum, tyArray, tyObject, 
+    tyBool, tyChar, tyEnum, tyArray, tyObject,
     tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc,
-    tyPointer, 
+    tyPointer,
     tyOpenArray, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
     tyUInt..tyUInt64}
   IntegralTypes* = {tyBool, tyChar, tyEnum, tyInt..tyInt64,
     tyFloat..tyFloat128, tyUInt..tyUInt64}
-  ConstantDataTypes*: TTypeKinds = {tyArrayConstr, tyArray, tySet, 
+  ConstantDataTypes*: TTypeKinds = {tyArrayConstr, tyArray, tySet,
                                     tyTuple, tySequence}
   NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence,
     tyProc, tyString, tyError}
@@ -883,7 +887,7 @@ const
     skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
   PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
                                       nfDotSetter, nfDotField,
-                                      nfIsRef}
+                                      nfIsRef, nfIsCursor}
   namePos* = 0
   patternPos* = 1    # empty except for term rewriting macros
   genericParamsPos* = 2
@@ -913,53 +917,7 @@ const
 
   skIterators* = {skIterator, skClosureIterator}
 
-  lfFullExternalName* = lfParamCopy # \
-    # only used when 'gCmd == cmdPretty': Indicates that the symbol has been
-    # imported via 'importc: "fullname"' and no format string.
-
-# creator procs:
-proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
-             info: TLineInfo): PSym
-proc newType*(kind: TTypeKind, owner: PSym): PType
-proc newNode*(kind: TNodeKind): PNode
-proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode
-proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode
-proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode
-proc newStrNode*(kind: TNodeKind, strVal: string): PNode
-proc newIdentNode*(ident: PIdent, info: TLineInfo): PNode
-proc newSymNode*(sym: PSym): PNode
-proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode
-proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode
-proc initStrTable*(x: var TStrTable)
-proc initTable*(x: var TTable)
-proc initIdTable*(x: var TIdTable)
-proc initObjectSet*(x: var TObjectSet)
-proc initIdNodeTable*(x: var TIdNodeTable)
-proc initNodeTable*(x: var TNodeTable)
-  
-# copy procs:
-proc copyType*(t: PType, owner: PSym, keepId: bool): PType
-proc copySym*(s: PSym, keepId: bool = false): PSym
-proc assignType*(dest, src: PType)
-proc copyStrTable*(dest: var TStrTable, src: TStrTable)
-proc copyTable*(dest: var TTable, src: TTable)
-proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet)
-proc copyIdTable*(dest: var TIdTable, src: TIdTable)
-proc sonsLen*(n: PNode): int {.inline.}
-proc sonsLen*(n: PType): int {.inline.}
-proc lastSon*(n: PNode): PNode {.inline.}
-proc lastSon*(n: PType): PType {.inline.}
-proc newSons*(father: PNode, length: int)
-proc newSons*(father: PType, length: int)
-proc addSon*(father, son: PNode)
-proc delSon*(father: PNode, idx: int)
-proc hasSonWith*(n: PNode, kind: TNodeKind): bool
-proc hasSubnodeWith*(n: PNode, kind: TNodeKind): bool
-proc replaceSons*(n: PNode, oldKind, newKind: TNodeKind)
-proc copyNode*(src: PNode): PNode
-  # does not copy its sons!
-proc copyTree*(src: PNode): PNode
-  # does copy its sons!
+var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
 
 proc isCallExpr*(n: PNode): bool =
   result = n.kind in nkCallKinds
@@ -969,17 +927,17 @@ proc discardSons*(father: PNode)
 proc len*(n: PNode): int {.inline.} =
   if isNil(n.sons): result = 0
   else: result = len(n.sons)
-  
+
 proc safeLen*(n: PNode): int {.inline.} =
   ## works even for leaves.
   if n.kind in {nkNone..nkNilLit} or isNil(n.sons): result = 0
   else: result = len(n.sons)
-  
+
 proc add*(father, son: PNode) =
   assert son != nil
   if isNil(father.sons): father.sons = @[]
   add(father.sons, son)
-  
+
 proc `[]`*(n: PNode, i: int): PNode {.inline.} =
   result = n.sons[i]
 
@@ -987,7 +945,58 @@ proc `[]`*(n: PNode, i: int): PNode {.inline.} =
 template `{}`*(n: PNode, i: int): expr = n[i -| n]
 template `{}=`*(n: PNode, i: int, s: PNode): stmt =
   n.sons[i -| n] = s
-  
+
+when defined(useNodeIds):
+  const nodeIdToDebug* = -1 # 299750 # 300761 #300863 # 300879
+  var gNodeId: int
+
+proc newNode*(kind: TNodeKind): PNode =
+  new(result)
+  result.kind = kind
+  #result.info = UnknownLineInfo() inlined:
+  result.info.fileIndex = int32(- 1)
+  result.info.col = int16(- 1)
+  result.info.line = int16(- 1)
+  when defined(useNodeIds):
+    result.id = gNodeId
+    if result.id == nodeIdToDebug:
+      echo "KIND ", result.kind
+      writeStackTrace()
+    inc gNodeId
+
+proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
+  result = newNode(kind)
+  result.intVal = intVal
+
+proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode =
+  result = newIntNode(kind, intVal)
+  result.typ = typ
+
+proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode =
+  result = newNode(kind)
+  result.floatVal = floatVal
+
+proc newStrNode*(kind: TNodeKind, strVal: string): PNode =
+  result = newNode(kind)
+  result.strVal = strVal
+
+proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
+             info: TLineInfo): PSym =
+  # generates a symbol and initializes the hash field too
+  new(result)
+  result.name = name
+  result.kind = symKind
+  result.flags = {}
+  result.info = info
+  result.options = gOptions
+  result.owner = owner
+  result.offset = - 1
+  result.id = getID()
+  when debugIds:
+    registerId(result)
+  #if result.id < 2000:
+  #  MessageOut(name.s & " has id: " & toString(result.id))
+
 var emptyNode* = newNode(nkEmpty)
 # There is a single empty node that is shared! Do not overwrite it!
 
@@ -1025,97 +1034,60 @@ proc appendToModule*(m: PSym, n: PNode) =
   else:
     assert m.ast.kind == nkStmtList
     m.ast.sons.add(n)
-  
+
 const                         # for all kind of hash tables:
   GrowthFactor* = 2           # must be power of 2, > 0
   StartSize* = 8              # must be power of 2, > 0
 
-proc copyStrTable(dest: var TStrTable, src: TStrTable) = 
+proc copyStrTable*(dest: var TStrTable, src: TStrTable) =
   dest.counter = src.counter
-  if isNil(src.data): return 
+  if isNil(src.data): return
   setLen(dest.data, len(src.data))
   for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
-  
-proc copyIdTable(dest: var TIdTable, src: TIdTable) = 
+
+proc copyIdTable*(dest: var TIdTable, src: TIdTable) =
   dest.counter = src.counter
-  if isNil(src.data): return 
+  if isNil(src.data): return
   newSeq(dest.data, len(src.data))
   for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
-  
-proc copyTable(dest: var TTable, src: TTable) = 
+
+proc copyTable*(dest: var TTable, src: TTable) =
   dest.counter = src.counter
-  if isNil(src.data): return 
+  if isNil(src.data): return
   setLen(dest.data, len(src.data))
   for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
-  
-proc copyObjectSet(dest: var TObjectSet, src: TObjectSet) = 
+
+proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
   dest.counter = src.counter
-  if isNil(src.data): return 
+  if isNil(src.data): return
   setLen(dest.data, len(src.data))
   for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
-  
-proc discardSons(father: PNode) = 
-  father.sons = nil
 
-when defined(useNodeIds):
-  const nodeIdToDebug* = -1 # 884953 # 612794
-  #612840 # 612905 # 614635 # 614637 # 614641
-  # 423408
-  #429107 # 430443 # 441048 # 441090 # 441153
-  var gNodeId: int
-
-proc newNode(kind: TNodeKind): PNode = 
-  new(result)
-  result.kind = kind
-  #result.info = UnknownLineInfo() inlined:
-  result.info.fileIndex = int32(- 1)
-  result.info.col = int16(- 1)
-  result.info.line = int16(- 1)
-  when defined(useNodeIds):
-    result.id = gNodeId
-    if result.id == nodeIdToDebug:
-      echo "KIND ", result.kind
-      writeStackTrace()
-    inc gNodeId
-
-proc newIntNode(kind: TNodeKind, intVal: BiggestInt): PNode = 
-  result = newNode(kind)
-  result.intVal = intVal
-
-proc newIntTypeNode(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode = 
-  result = newIntNode(kind, intVal)
-  result.typ = typ
-
-proc newFloatNode(kind: TNodeKind, floatVal: BiggestFloat): PNode = 
-  result = newNode(kind)
-  result.floatVal = floatVal
-
-proc newStrNode(kind: TNodeKind, strVal: string): PNode = 
-  result = newNode(kind)
-  result.strVal = strVal
+proc discardSons*(father: PNode) =
+  father.sons = nil
 
 proc withInfo*(n: PNode, info: TLineInfo): PNode =
   n.info = info
   return n
 
-proc newIdentNode(ident: PIdent, info: TLineInfo): PNode = 
+proc newIdentNode*(ident: PIdent, info: TLineInfo): PNode =
   result = newNode(nkIdent)
   result.ident = ident
   result.info = info
 
-proc newSymNode(sym: PSym): PNode = 
+proc newSymNode*(sym: PSym): PNode =
   result = newNode(nkSym)
   result.sym = sym
   result.typ = sym.typ
   result.info = sym.info
 
-proc newSymNode*(sym: PSym, info: TLineInfo): PNode = 
+proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
   result = newNode(nkSym)
   result.sym = sym
   result.typ = sym.typ
   result.info = info
 
-proc newNodeI(kind: TNodeKind, info: TLineInfo): PNode =
+proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode =
   new(result)
   result.kind = kind
   result.info = info
@@ -1154,11 +1126,16 @@ proc newNode*(kind: TNodeKind, info: TLineInfo, sons: TNodeSeq = @[],
       writeStackTrace()
     inc gNodeId
 
-proc newNodeIT(kind: TNodeKind, info: TLineInfo, typ: PType): PNode = 
+proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
   result = newNode(kind)
   result.info = info
   result.typ = typ
 
+proc addSon*(father, son: PNode) =
+  assert son != nil
+  if isNil(father.sons): father.sons = @[]
+  add(father.sons, son)
+
 var emptyParams = newNode(nkFormalParams)
 emptyParams.addSon(emptyNode)
 
@@ -1180,7 +1157,7 @@ proc `$`*(x: TLockLevel): string =
   elif x.ord == UnknownLockLevel.ord: result = "<unknown>"
   else: result = $int16(x)
 
-proc newType(kind: TTypeKind, owner: PSym): PType = 
+proc newType*(kind: TTypeKind, owner: PSym): PType =
   new(result)
   result.kind = kind
   result.owner = owner
@@ -1192,7 +1169,7 @@ proc newType(kind: TTypeKind, owner: PSym): PType =
     registerId(result)
   #if result.id < 2000:
   #  messageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id))
-  
+
 proc mergeLoc(a: var TLoc, b: TLoc) =
   if a.k == low(a.k): a.k = b.k
   if a.s == low(a.s): a.s = b.s
@@ -1200,8 +1177,38 @@ proc mergeLoc(a: var TLoc, b: TLoc) =
   if a.t == nil: a.t = b.t
   if a.r == nil: a.r = b.r
   #if a.a == 0: a.a = b.a
-  
-proc assignType(dest, src: PType) = 
+
+proc newSons*(father: PNode, length: int) =
+  if isNil(father.sons):
+    newSeq(father.sons, length)
+  else:
+    setLen(father.sons, length)
+
+proc newSons*(father: PType, length: int) =
+  if isNil(father.sons):
+    newSeq(father.sons, length)
+  else:
+    setLen(father.sons, length)
+
+proc sonsLen*(n: PType): int =
+  if isNil(n.sons): result = 0
+  else: result = len(n.sons)
+
+proc len*(n: PType): int =
+  if isNil(n.sons): result = 0
+  else: result = len(n.sons)
+
+proc sonsLen*(n: PNode): int =
+  if isNil(n.sons): result = 0
+  else: result = len(n.sons)
+
+proc lastSon*(n: PNode): PNode =
+  result = n.sons[sonsLen(n) - 1]
+
+proc lastSon*(n: PType): PType =
+  result = n.sons[sonsLen(n) - 1]
+
+proc assignType*(dest, src: PType) =
   dest.kind = src.kind
   dest.flags = src.flags
   dest.callConv = src.callConv
@@ -1221,23 +1228,23 @@ proc assignType(dest, src: PType) =
       dest.sym = src.sym
   newSons(dest, sonsLen(src))
   for i in countup(0, sonsLen(src) - 1): dest.sons[i] = src.sons[i]
-  
-proc copyType(t: PType, owner: PSym, keepId: bool): PType = 
+
+proc copyType*(t: PType, owner: PSym, keepId: bool): PType =
   result = newType(t.kind, owner)
   assignType(result, t)
-  if keepId: 
+  if keepId:
     result.id = t.id
-  else: 
+  else:
     when debugIds: registerId(result)
   result.sym = t.sym          # backend-info should not be copied
-  
-proc copySym(s: PSym, keepId: bool = false): PSym = 
+
+proc copySym*(s: PSym, keepId: bool = false): PSym =
   result = newSym(s.kind, s.name, s.owner, s.info)
   #result.ast = nil            # BUGFIX; was: s.ast which made problems
   result.typ = s.typ
   if keepId:
     result.id = s.id
-  else: 
+  else:
     result.id = getID()
     when debugIds: registerId(result)
   result.flags = s.flags
@@ -1264,74 +1271,39 @@ proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo): PSym =
   result.annex = s.annex
   # XXX once usedGenerics is used, ensure module aliases keep working!
   assert s.usedGenerics == nil
-  
-proc newSym(symKind: TSymKind, name: PIdent, owner: PSym,
-            info: TLineInfo): PSym = 
-  # generates a symbol and initializes the hash field too
-  new(result)
-  result.name = name
-  result.kind = symKind
-  result.flags = {}
-  result.info = info
-  result.options = gOptions
-  result.owner = owner
-  result.offset = - 1
-  result.id = getID()
-  when debugIds: 
-    registerId(result)
-  #if result.id < 2000:
-  #  MessageOut(name.s & " has id: " & toString(result.id))
-  
-proc initStrTable(x: var TStrTable) = 
+
+proc initStrTable*(x: var TStrTable) =
   x.counter = 0
   newSeq(x.data, StartSize)
 
 proc newStrTable*: TStrTable =
   initStrTable(result)
 
-proc initTable(x: var TTable) = 
+proc initTable(x: var TTable) =
   x.counter = 0
   newSeq(x.data, StartSize)
 
-proc initIdTable(x: var TIdTable) = 
+proc initIdTable*(x: var TIdTable) =
   x.counter = 0
   newSeq(x.data, StartSize)
 
-proc initObjectSet(x: var TObjectSet) = 
+proc resetIdTable*(x: var TIdTable) =
   x.counter = 0
-  newSeq(x.data, StartSize)
+  # clear and set to old initial size:
+  setLen(x.data, 0)
+  setLen(x.data, StartSize)
 
-proc initIdNodeTable(x: var TIdNodeTable) = 
+proc initObjectSet*(x: var TObjectSet) =
   x.counter = 0
   newSeq(x.data, StartSize)
 
-proc initNodeTable(x: var TNodeTable) = 
+proc initIdNodeTable*(x: var TIdNodeTable) =
   x.counter = 0
   newSeq(x.data, StartSize)
 
-proc sonsLen(n: PType): int = 
-  if isNil(n.sons): result = 0
-  else: result = len(n.sons)
-
-proc len*(n: PType): int = 
-  if isNil(n.sons): result = 0
-  else: result = len(n.sons)
-  
-proc newSons(father: PType, length: int) = 
-  if isNil(father.sons): 
-    newSeq(father.sons, length)
-  else:
-    setLen(father.sons, length)
-
-proc sonsLen(n: PNode): int = 
-  if isNil(n.sons): result = 0
-  else: result = len(n.sons)
-  
-proc newSons(father: PNode, length: int) = 
-  if isNil(father.sons): 
-    newSeq(father.sons, length)
-  else:
-    setLen(father.sons, length)
+proc initNodeTable*(x: var TNodeTable) =
+  x.counter = 0
+  newSeq(x.data, StartSize)
 
 proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
   ## Used throughout the compiler code to test whether a type tree contains or
@@ -1346,26 +1318,27 @@ proc isGCedMem*(t: PType): bool {.inline.} =
            t.kind == tyProc and t.callConv == ccClosure
 
 proc propagateToOwner*(owner, elem: PType) =
-  const HaveTheirOwnEmpty = {tySequence, tySet}
-  owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta})
+  const HaveTheirOwnEmpty = {tySequence, tySet, tyPtr, tyRef, tyProc}
+  owner.flags = owner.flags + (elem.flags * {tfHasMeta})
   if tfNotNil in elem.flags:
-    if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvokation}:
+    if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}:
       owner.flags.incl tfNotNil
     elif owner.kind notin HaveTheirOwnEmpty:
       owner.flags.incl tfNeedsInit
-  
+
   if tfNeedsInit in elem.flags:
     if owner.kind in HaveTheirOwnEmpty: discard
     else: owner.flags.incl tfNeedsInit
-    
-  if tfShared in elem.flags:
-    owner.flags.incl tfHasShared
 
   if elem.isMetaType:
     owner.flags.incl tfHasMeta
 
-  if owner.kind != tyProc:
-    if elem.isGCedMem or tfHasGCedMem in elem.flags:
+  if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
+                       tyGenericInvocation}:
+    let elemB = elem.skipTypes({tyGenericInst})
+    if elemB.isGCedMem or tfHasGCedMem in elemB.flags:
+      # for simplicity, we propagate this flag even to generics. We then
+      # ensure this doesn't bite us in sempass2.
       owner.flags.incl tfHasGCedMem
 
 proc rawAddSon*(father, son: PType) =
@@ -1373,24 +1346,19 @@ proc rawAddSon*(father, son: PType) =
   add(father.sons, son)
   if not son.isNil: propagateToOwner(father, son)
 
-proc addSon(father, son: PNode) = 
-  assert son != nil
-  if isNil(father.sons): father.sons = @[]
-  add(father.sons, son)
-
 proc addSonNilAllowed*(father, son: PNode) =
   if isNil(father.sons): father.sons = @[]
   add(father.sons, son)
 
-proc delSon(father: PNode, idx: int) = 
-  if isNil(father.sons): return 
+proc delSon*(father: PNode, idx: int) =
+  if isNil(father.sons): return
   var length = sonsLen(father)
   for i in countup(idx, length - 2): father.sons[i] = father.sons[i + 1]
   setLen(father.sons, length - 1)
 
-proc copyNode(src: PNode): PNode = 
+proc copyNode*(src: PNode): PNode =
   # does not copy its sons!
-  if src == nil: 
+  if src == nil:
     return nil
   result = newNode(src.kind)
   result.info = src.info
@@ -1407,7 +1375,7 @@ proc copyNode(src: PNode): PNode =
   of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
   else: discard
 
-proc shallowCopy*(src: PNode): PNode = 
+proc shallowCopy*(src: PNode): PNode =
   # does not copy its sons, but provides space for them:
   if src == nil: return nil
   result = newNode(src.kind)
@@ -1425,9 +1393,9 @@ proc shallowCopy*(src: PNode): PNode =
   of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
   else: newSeq(result.sons, sonsLen(src))
 
-proc copyTree(src: PNode): PNode = 
+proc copyTree*(src: PNode): PNode =
   # copy a whole syntax tree; performs deep copying
-  if src == nil: 
+  if src == nil:
     return nil
   result = newNode(src.kind)
   result.info = src.info
@@ -1442,26 +1410,20 @@ proc copyTree(src: PNode): PNode =
   of nkSym: result.sym = src.sym
   of nkIdent: result.ident = src.ident
   of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
-  else: 
+  else:
     newSeq(result.sons, sonsLen(src))
-    for i in countup(0, sonsLen(src) - 1): 
+    for i in countup(0, sonsLen(src) - 1):
       result.sons[i] = copyTree(src.sons[i])
-  
-proc lastSon(n: PNode): PNode = 
-  result = n.sons[sonsLen(n) - 1]
-
-proc lastSon(n: PType): PType = 
-  result = n.sons[sonsLen(n) - 1]
 
-proc hasSonWith(n: PNode, kind: TNodeKind): bool = 
-  for i in countup(0, sonsLen(n) - 1): 
-    if n.sons[i].kind == kind: 
+proc hasSonWith*(n: PNode, kind: TNodeKind): bool =
+  for i in countup(0, sonsLen(n) - 1):
+    if n.sons[i].kind == kind:
       return true
   result = false
 
-proc hasNilSon*(n: PNode): bool = 
-  for i in countup(0, safeLen(n) - 1): 
-    if n.sons[i] == nil: 
+proc hasNilSon*(n: PNode): bool =
+  for i in countup(0, safeLen(n) - 1):
+    if n.sons[i] == nil:
       return true
     elif hasNilSon(n.sons[i]):
       return true
@@ -1475,47 +1437,47 @@ proc containsNode*(n: PNode, kinds: TNodeKinds): bool =
     for i in countup(0, sonsLen(n) - 1):
       if n.kind in kinds or containsNode(n.sons[i], kinds): return true
 
-proc hasSubnodeWith(n: PNode, kind: TNodeKind): bool = 
+proc hasSubnodeWith*(n: PNode, kind: TNodeKind): bool =
   case n.kind
   of nkEmpty..nkNilLit: result = n.kind == kind
-  else: 
-    for i in countup(0, sonsLen(n) - 1): 
-      if (n.sons[i].kind == kind) or hasSubnodeWith(n.sons[i], kind): 
+  else:
+    for i in countup(0, sonsLen(n) - 1):
+      if (n.sons[i].kind == kind) or hasSubnodeWith(n.sons[i], kind):
         return true
     result = false
 
-proc replaceSons(n: PNode, oldKind, newKind: TNodeKind) = 
-  for i in countup(0, sonsLen(n) - 1): 
+proc replaceSons(n: PNode, oldKind, newKind: TNodeKind) =
+  for i in countup(0, sonsLen(n) - 1):
     if n.sons[i].kind == oldKind: n.sons[i].kind = newKind
-  
-proc sonsNotNil(n: PNode): bool = 
-  for i in countup(0, sonsLen(n) - 1): 
-    if n.sons[i] == nil: 
+
+proc sonsNotNil(n: PNode): bool =
+  for i in countup(0, sonsLen(n) - 1):
+    if n.sons[i] == nil:
       return false
   result = true
 
-proc getInt*(a: PNode): BiggestInt = 
+proc getInt*(a: PNode): BiggestInt =
   case a.kind
   of nkIntLit..nkUInt64Lit: result = a.intVal
-  else: 
+  else:
     internalError(a.info, "getInt")
     result = 0
 
-proc getFloat*(a: PNode): BiggestFloat = 
+proc getFloat*(a: PNode): BiggestFloat =
   case a.kind
   of nkFloatLit..nkFloat128Lit: result = a.floatVal
-  else: 
+  else:
     internalError(a.info, "getFloat")
     result = 0.0
 
-proc getStr*(a: PNode): string = 
+proc getStr*(a: PNode): string =
   case a.kind
   of nkStrLit..nkTripleStrLit: result = a.strVal
-  else: 
+  else:
     internalError(a.info, "getStr")
     result = ""
 
-proc getStrOrChar*(a: PNode): string = 
+proc getStrOrChar*(a: PNode): string =
   case a.kind
   of nkStrLit..nkTripleStrLit: result = a.strVal
   of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal))
@@ -1523,7 +1485,7 @@ proc getStrOrChar*(a: PNode): string =
     internalError(a.info, "getStrOrChar")
     result = ""
 
-proc isGenericRoutine*(s: PSym): bool = 
+proc isGenericRoutine*(s: PSym): bool =
   case s.kind
   of skProcKinds:
     result = sfFromGeneric in s.flags or
@@ -1567,3 +1529,11 @@ proc makeStmtList*(n: PNode): PNode =
   else:
     result = newNodeI(nkStmtList, n.info)
     result.add n
+
+proc createMagic*(name: string, m: TMagic): PSym =
+  result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
+  result.magic = m
+
+let
+  opNot* = createMagic("not", mNot)
+  opContains* = createMagic("contains", mInSet)