summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2018-05-16 03:06:07 +0200
committerAraq <rumpf_a@web.de>2018-05-16 03:06:07 +0200
commit2a7fc84c86c48c6ca6354c8c2f9232c3f1a0b049 (patch)
tree2831e8d6b75991bc522660ac7de406c1168d3050
parent479212995a615fc0a0827cc27756bb8cf865c1b0 (diff)
downloadNim-2a7fc84c86c48c6ca6354c8c2f9232c3f1a0b049.tar.gz
remove ast.emptyNode global; cleanup configuration.nim
-rw-r--r--compiler/ast.nim10
-rw-r--r--compiler/cgmeth.nim6
-rw-r--r--compiler/configuration.nim178
-rw-r--r--compiler/destroyer.nim8
-rw-r--r--compiler/evalffi.nim4
-rw-r--r--compiler/evaltempl.nim2
-rw-r--r--compiler/lambdalifting.nim4
-rw-r--r--compiler/lowerings.nim22
-rw-r--r--compiler/modulegraphs.nim2
-rw-r--r--compiler/parser.nim92
-rw-r--r--compiler/plugins/itersgen.nim8
-rw-r--r--compiler/rodread.nim2
-rw-r--r--compiler/sem.nim2
-rw-r--r--compiler/semasgn.nim4
-rw-r--r--compiler/semexprs.nim29
-rw-r--r--compiler/semfields.nim2
-rw-r--r--compiler/seminst.nim2
-rw-r--r--compiler/semmagic.nim2
-rw-r--r--compiler/semparallel.nim2
-rw-r--r--compiler/sempass2.nim4
-rw-r--r--compiler/semstmts.nim16
-rw-r--r--compiler/semtypes.nim2
-rw-r--r--compiler/sigmatch.nim9
-rw-r--r--compiler/suggest.nim2
-rw-r--r--compiler/syntaxes.nim4
-rw-r--r--compiler/transf.nim10
-rw-r--r--compiler/vm.nim6
-rw-r--r--compiler/vmdeps.nim22
-rw-r--r--compiler/vmgen.nim4
29 files changed, 148 insertions, 312 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 5a84b2b02..ec727544e 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -1095,9 +1095,6 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
   #  writeStacktrace()
   #  MessageOut(name.s & " has id: " & toString(result.id))
 
-var emptyNode* = newNode(nkEmpty) # XXX global variable here!
-# There is a single empty node that is shared! Do not overwrite it!
-
 proc isMetaType*(t: PType): bool =
   return t.kind in tyMetaTypes or
          (t.kind == tyStatic and t.n == nil) or
@@ -1229,13 +1226,10 @@ proc addSon*(father, son: PNode) =
   if isNil(father.sons): father.sons = @[]
   add(father.sons, son)
 
-var emptyParams = newNode(nkFormalParams)
-emptyParams.addSon(emptyNode)
-
 proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
-                 params = emptyParams,
+                 params,
                  name, pattern, genericParams,
-                 pragmas, exceptions = ast.emptyNode): PNode =
+                 pragmas, exceptions: PNode): PNode =
   result = newNodeI(kind, info)
   result.sons = @[name, pattern, genericParams, params,
                   pragmas, exceptions, body]
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index 1d72952e2..c739381bb 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -115,7 +115,7 @@ proc createDispatcher(s: PSym): PSym =
   # we can't inline the dispatcher itself (for now):
   if disp.typ.callConv == ccInline: disp.typ.callConv = ccDefault
   disp.ast = copyTree(s.ast)
-  disp.ast.sons[bodyPos] = ast.emptyNode
+  disp.ast.sons[bodyPos] = newNodeI(nkEmpty, s.info)
   disp.loc.r = nil
   if s.typ.sons[0] != nil:
     if disp.ast.sonsLen > resultPos:
@@ -124,7 +124,7 @@ proc createDispatcher(s: PSym): PSym =
       # We've encountered a method prototype without a filled-in
       # resultPos slot. We put a placeholder in there that will
       # be updated in fixupDispatcher().
-      disp.ast.addSon(ast.emptyNode)
+      disp.ast.addSon(newNodeI(nkEmpty, s.info))
   attachDispatcher(s, newSymNode(disp))
   # attach to itself to prevent bugs:
   attachDispatcher(disp, newSymNode(disp))
@@ -137,7 +137,7 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
   # the lock level of the dispatcher needs to be updated/checked
   # against that of the method.
   if disp.ast.sonsLen > resultPos and meth.ast.sonsLen > resultPos and
-     disp.ast.sons[resultPos] == ast.emptyNode:
+     disp.ast.sons[resultPos].kind == nkEmpty:
     disp.ast.sons[resultPos] = copyTree(meth.ast.sons[resultPos])
 
   # The following code works only with lock levels, so we disable
diff --git a/compiler/configuration.nim b/compiler/configuration.nim
index f9f0e623c..bd9651c08 100644
--- a/compiler/configuration.nim
+++ b/compiler/configuration.nim
@@ -11,8 +11,6 @@
 ## needs to be passed around to everything so that the compiler becomes
 ## more useful as a library.
 
-import tables
-
 const
   explanationsBaseUrl* = "https://nim-lang.org/docs/manual"
 
@@ -182,179 +180,3 @@ const
 const
   errXMustBeCompileTime* = "'$1' can only be used in compile-time context"
   errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected"
-
-#[
-errStringLiteralExpected: "string literal expected",
-errIntLiteralExpected: "integer literal expected",
-errIdentifierExpected: "identifier expected, but found '$1'",
-errNewlineExpected: "newline expected, but found '$1'",
-errInvalidModuleName: "invalid module name: '$1'",
-errOnOrOffExpected: "'on' or 'off' expected",
-errNoneSpeedOrSizeExpected: "'none', 'speed' or 'size' expected",
-errInvalidPragma: "invalid pragma",
-errUnknownPragma: "unknown pragma: '$1'",
-errAtPopWithoutPush: "'pop' without a 'push' pragma",
-errEmptyAsm: "empty asm statement",
-errInvalidIndentation: "invalid indentation",
-
-errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type",
-errAttemptToRedefine: ,
-errStmtInvalidAfterReturn: "statement not allowed after 'return', 'break', 'raise', 'continue' or proc call with noreturn pragma",
-errStmtExpected: "statement expected",
-errInvalidLabel: "'$1' is no label",
-errInvalidCmdLineOption: "invalid command line option: '$1'",
-errCmdLineArgExpected: "argument for command line option expected: '$1'",
-errCmdLineNoArgExpected: "invalid argument for command line option: '$1'",
-errInvalidVarSubstitution: "invalid variable substitution in '$1'",
-errUnknownVar: "unknown variable: '$1'",
-errUnknownCcompiler: "unknown C compiler: '$1'",
-errOnOrOffExpectedButXFound: "'on' or 'off' expected, but '$1' found",
-errOnOffOrListExpectedButXFound: "'on', 'off' or 'list' expected, but '$1' found",
-errGenOutExpectedButXFound: "'c', 'c++' or 'yaml' expected, but '$1' found",
-,
-errInvalidMultipleAsgn: "multiple assignment is not allowed",
-errColonOrEqualsExpected: "':' or '=' expected, but found '$1'",
-errUndeclaredField: "undeclared field: '$1'",
-errUndeclaredRoutine: "attempting to call undeclared routine: '$1'",
-errUseQualifier: "ambiguous identifier: '$1' -- use a qualifier",
-errTypeExpected: "type expected",
-errSystemNeeds: "system module needs '$1'",
-errExecutionOfProgramFailed: "execution of an external program failed: '$1'",
-errNotOverloadable: ,
-errInvalidArgForX: "invalid argument for '$1'",
-errStmtHasNoEffect: "statement has no effect",
-,
-errXExpectsArrayType: "'$1' expects an array type",
-errIteratorCannotBeInstantiated: "'$1' cannot be instantiated because its body has not been compiled yet",
-errExprXAmbiguous: "expression '$1' ambiguous in this context",
-errConstantDivisionByZero: ,
-errOrdinalOrFloatTypeExpected: "ordinal or float type expected",
-errOverOrUnderflow: ,
-errCannotEvalXBecauseIncompletelyDefined: ,
-errChrExpectsRange0_255: "'chr' expects an int in the range 0..255",
-errDynlibRequiresExportc: "'dynlib' requires 'exportc'",
-errNilAccess: "attempt to access a nil address",
-errIndexOutOfBounds: "index out of bounds",
-errIndexTypesDoNotMatch: "index types do not match",
-errBracketsInvalidForType: "'[]' operator invalid for this type",
-errValueOutOfSetBounds: "value out of set bounds",
-errFieldNotInit: "field '$1' not initialized",
-errExprXCannotBeCalled: "expression '$1' cannot be called",
-errExprHasNoType: "expression has no type",
-errExprXHasNoType:,
-errCastNotInSafeMode: "'cast' not allowed in safe mode",
-errExprCannotBeCastToX: ,
-errCommaOrParRiExpected: "',' or ')' expected",
-errCurlyLeOrParLeExpected: "'{' or '(' expected",
-errSectionExpected: "section ('type', 'proc', etc.) expected",
-errRangeExpected: "range expected",
-errMagicOnlyInSystem: "'magic' only allowed in system module",
-errPowerOfTwoExpected: "power of two expected",
-errStringMayNotBeEmpty: "string literal may not be empty",
-errCallConvExpected: "calling convention expected",
-errProcOnlyOneCallConv: "a proc can only have one calling convention",
-errSymbolMustBeImported: "symbol must be imported if 'lib' pragma is used",
-errExprMustBeBool: "expression must be of type 'bool'",
-errConstExprExpected: "constant expression expected",
-errDuplicateCaseLabel: "duplicate case label",
-errRangeIsEmpty: "range is empty",
-,
-errSelectorMustBeOrdinal: "selector must be of an ordinal type",
-errOrdXMustNotBeNegative: "ord($1) must not be negative",
-errLenXinvalid: "len($1) must be less than 32768",
-errTypeXhasUnknownSize: "type '$1' has unknown size",
-errConstNeedsConstExpr: "a constant can only be initialized with a constant expression",
-errConstNeedsValue: "a constant needs a value",
-errResultCannotBeOpenArray: "the result type cannot be on open array",
-errSizeTooBig: "computing the type's size produced an overflow",
-errInheritanceOnlyWithEnums: "inheritance only works with an enum",
-errIllegalRecursionInTypeX:,
-errCannotInstantiateX: "cannot instantiate: '$1'",
-errTypeMismatch: "type mismatch: got <",
-errButExpected: "but expected one of: ",
-errButExpectedX: "but expected '$1'",
-errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3",
-errWrongNumberOfArguments: "wrong number of arguments",
-errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'",
-errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters",
-errXCannotBePassedToProcVar: ,
-,
-errImplOfXexpected: ,
-
-errIllegalConvFromXtoY: ,
-errCannotBindXTwice: "cannot bind parameter '$1' twice",
-errInvalidOrderInArrayConstructor: ,
-errInvalidOrderInEnumX: "invalid order in enum '$1'",
-errEnumXHasHoles: "enum '$1' has holes",
-errExceptExpected: "'except' or 'finally' expected",
-errInvalidTry: "after catch all 'except' or 'finally' no section may follow",
-errOptionExpected: ,
-errXisNoLabel: "'$1' is not a label",
-errNotAllCasesCovered: "not all cases are covered",
-errUnknownSubstitionVar: "unknown substitution variable: '$1'",
-errComplexStmtRequiresInd: "complex statement requires indentation",
-errXisNotCallable: "'$1' is not callable",
-errNoPragmasAllowedForX: "no pragmas allowed for $1",
-,
-errInvalidParamKindX: "invalid param kind: '$1'",
-errDefaultArgumentInvalid: "default argument invalid",
-errNamedParamHasToBeIdent: "named parameter has to be an identifier",
-errNoReturnTypeForX: "no return type allowed for $1",
-errConvNeedsOneArg: "a type conversion needs exactly one argument",
-errInvalidPragmaX: ,
-errXNotAllowedHere: "$1 not allowed here",
-errXisNoType: "invalid type: '$1'",
-errCircumNeedsPointer: "'[]' needs a pointer or reference type",
-errInvalidExpression: "invalid expression",
-errInvalidExpressionX: "invalid expression: '$1'",
-errEnumHasNoValueX: "enum has no value '$1'",
-,
-errNoCommand: "no command given",
-errInvalidCommandX: "invalid command: '$1'",
-errXNeedsParamObjectType: ,
-errTemplateInstantiationTooNested: "template instantiation too nested, try --evalTemplateLimit:N",
-errMacroInstantiationTooNested: "macro instantiation too nested, try --evalMacroLimit:N",
-errInstantiationFrom: "template/generic instantiation from here",
-errInvalidIndexValueForTuple: "invalid index value for tuple subscript",
-errCommandExpectsFilename: "command expects a filename argument",
-errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
-errXExpected: "'$1' expected",
-,
-errCastToANonConcreteType: "cannot cast to a non concrete type: '$1'",
-errInvalidSectionStart: "invalid section start",
-errGridTableNotImplemented: "grid table is not implemented",
-errGeneralParseError: "general parse error",
-errNewSectionExpected: "new section expected",
-errWhitespaceExpected: "whitespace expected, got '$1'",
-errXisNoValidIndexFile: "'$1' is no valid index file",
-errCannotRenderX: "cannot render reStructuredText element '$1'",
-errVarVarTypeNotAllowed: ,
-errInstantiateXExplicitly: "instantiate '$1' explicitly",
-errOnlyACallOpCanBeDelegator: ,
-errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
-errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " &
-                                   "because the parameter '$1' has a generic type",
-errDestructorNotGenericEnough: "Destructor signature is too specific. " &
-                               "A destructor must be associated will all instantiations of a generic type",
-errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " &
-                                "templates, macros and other inline iterators",
-errXExpectsTwoArguments: "'$1' expects two arguments",
-errXExpectsObjectTypes: "'$1' expects object types",
-errXcanNeverBeOfThisSubtype: "'$1' can never be of this subtype",
-errTooManyIterations: "interpretation requires too many iterations; " &
-  "if you are sure this is not a bug in your code edit " &
-  "compiler/vmdef.MaxLoopIterations and rebuild the compiler",
-errFieldXNotFound: "field '$1' cannot be found",
-errInvalidConversionFromTypeX: "invalid conversion from type '$1'",
-errAssertionFailed: "assertion failed",
-errCannotGenerateCodeForX: "cannot generate code for '$1'",
-errXRequiresOneArgument: "$1 requires one parameter",
-errUnhandledExceptionX: "unhandled exception: $1",
-errCyclicTree: "macro returned a cyclic abstract syntax tree",
-errXisNoMacroOrTemplate: "'$1' is no macro or template",
-errXhasSideEffects: "'$1' can have side effects",
-errWrongSymbolX:,
-errIllegalCaptureX: "illegal capture '$1'",
-errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
-,
-]#
diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim
index 31c735794..0dc90b552 100644
--- a/compiler/destroyer.nim
+++ b/compiler/destroyer.nim
@@ -132,6 +132,7 @@ type
     destroys, topLevelVars: PNode
     toDropBit: Table[int, PSym]
     graph: ModuleGraph
+    emptyNode: PNode
 
 proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode =
   # XXX why are temps fields in an object here?
@@ -243,7 +244,7 @@ proc genDestroy(c: Con; t: PType; dest: PNode): PNode =
   genOp(t.destructor, "=destroy")
 
 proc addTopVar(c: var Con; v: PNode) =
-  c.topLevelVars.add newTree(nkIdentDefs, v, emptyNode, emptyNode)
+  c.topLevelVars.add newTree(nkIdentDefs, v, c.emptyNode, c.emptyNode)
 
 proc dropBit(c: var Con; s: PSym): PSym =
   result = c.toDropBit.getOrDefault(s.id)
@@ -253,7 +254,7 @@ proc registerDropBit(c: var Con; s: PSym) =
   let result = newSym(skTemp, getIdent(s.name.s & "_AliveBit"), c.owner, s.info)
   result.typ = getSysType(c.graph, s.info, tyBool)
   let trueVal = newIntTypeNode(nkIntLit, 1, result.typ)
-  c.topLevelVars.add newTree(nkIdentDefs, newSymNode result, emptyNode, trueVal)
+  c.topLevelVars.add newTree(nkIdentDefs, newSymNode result, c.emptyNode, trueVal)
   c.toDropBit[s.id] = result
   # generate:
   #  if not sinkParam_AliveBit: `=destroy`(sinkParam)
@@ -328,7 +329,7 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode =
 
   var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
   vpart.sons[0] = tempAsNode
-  vpart.sons[1] = ast.emptyNode
+  vpart.sons[1] = c.emptyNode
   vpart.sons[2] = n
   add(v, vpart)
 
@@ -434,6 +435,7 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
   c.topLevelVars = newNodeI(nkVarSection, n.info)
   c.toDropBit = initTable[int, PSym]()
   c.graph = g
+  c.emptyNode = newNodeI(nkEmpty, n.info)
   let cfg = constructCfg(owner, n)
   shallowCopy(c.g, cfg)
   c.jumpTargets = initIntSet()
diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim
index 0e3d0609d..e863c8995 100644
--- a/compiler/evalffi.nim
+++ b/compiler/evalffi.nim
@@ -442,7 +442,7 @@ proc callForeignFunction*(call: PNode): PNode =
   libffi.call(cif, fn, retVal, args)
 
   if retVal.isNil:
-    result = emptyNode
+    result = newNode(nkEmpty)
   else:
     result = unpack(retVal, typ.sons[0], nil)
     result.info = call.info
@@ -484,7 +484,7 @@ proc callForeignFunction*(fn: PNode, fntyp: PType,
   libffi.call(cif, fn, retVal, cargs)
 
   if retVal.isNil:
-    result = emptyNode
+    result = newNode(nkEmpty)
   else:
     result = unpack(retVal, fntyp.sons[0], nil)
     result.info = info
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index 01c56ec9c..a1b5e731c 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -104,7 +104,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode
     let default = s.typ.n.sons[i].sym.ast
     if default.isNil or default.kind == nkEmpty:
       localError(conf, n.info, errWrongNumberOfArguments)
-      addSon(result, ast.emptyNode)
+      addSon(result, newNodeI(nkEmpty, n.info))
     else:
       addSon(result, default.copyTree)
 
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 773e5e29c..2789e4b8f 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -629,7 +629,7 @@ proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
     addSon(a, retVal)
     retStmt.add(a)
   else:
-    retStmt.add(emptyNode)
+    retStmt.add(newNodeI(nkEmpty, n.info))
 
   var stateLabelStmt = newNodeI(nkState, n.info)
   stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo,
@@ -923,7 +923,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
       body[i].sym.kind = skLet
     addSon(vpart, body[i])
 
-  addSon(vpart, ast.emptyNode) # no explicit type
+  addSon(vpart, newNodeI(nkEmpty, body.info)) # no explicit type
   if not env.isNil:
     call.sons[0] = makeClosure(g, call.sons[0].sym, env.newSymNode, body.info)
   addSon(vpart, call)
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index 13336f00e..3f855c1eb 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -30,8 +30,8 @@ proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
 proc addVar*(father, v: PNode) =
   var vpart = newNodeI(nkIdentDefs, v.info, 3)
   vpart.sons[0] = v
-  vpart.sons[1] = ast.emptyNode
-  vpart.sons[2] = ast.emptyNode
+  vpart.sons[1] = newNodeI(nkEmpty, v.info)
+  vpart.sons[2] = vpart[1]
   addSon(father, vpart)
 
 proc newAsgnStmt(le, ri: PNode): PNode =
@@ -83,7 +83,7 @@ proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode =
 
   var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
   vpart.sons[0] = tempAsNode
-  vpart.sons[1] = ast.emptyNode
+  vpart.sons[1] = newNodeI(nkEmpty, value.info)
   vpart.sons[2] = value
   addSon(v, vpart)
   result.add(v)
@@ -104,7 +104,7 @@ proc lowerSwap*(n: PNode; owner: PSym): PNode =
 
   var vpart = newNodeI(nkIdentDefs, v.info, 3)
   vpart.sons[0] = tempAsNode
-  vpart.sons[1] = ast.emptyNode
+  vpart.sons[1] = newNodeI(nkEmpty, v.info)
   vpart.sons[2] = n[1]
   addSon(v, vpart)
 
@@ -344,8 +344,8 @@ proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: P
 
   var vpart = newNodeI(nkIdentDefs, varSection.info, 3)
   vpart.sons[0] = newSymNode(result)
-  vpart.sons[1] = ast.emptyNode
-  vpart.sons[2] = if varInit.isNil: v else: ast.emptyNode
+  vpart.sons[1] = newNodeI(nkEmpty, varSection.info)
+  vpart.sons[2] = if varInit.isNil: v else: vpart[1]
   varSection.add vpart
   if varInit != nil:
     if useShallowCopy and typeNeedsNoDeepCopy(typ):
@@ -438,7 +438,7 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
     body.add callCodegenProc(g, "barrierLeave", threadLocalBarrier.newSymNode)
 
   var params = newNodeI(nkFormalParams, f.info)
-  params.add emptyNode
+  params.add newNodeI(nkEmpty, f.info)
   params.add threadParam.newSymNode
   params.add argsParam.newSymNode
 
@@ -454,12 +454,16 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
   let name = (if f.kind == nkSym: f.sym.name.s else: genPrefix) & "Wrapper"
   result = newSym(skProc, getIdent(name), argsParam.owner, f.info,
                   argsParam.options)
-  result.ast = newProcNode(nkProcDef, f.info, body, params, newSymNode(result))
+  let emptyNode = newNodeI(nkEmpty, f.info)
+  result.ast = newProcNode(nkProcDef, f.info, body = body,
+      params = params, name = newSymNode(result), pattern = emptyNode,
+      genericParams = emptyNode, pragmas = emptyNode,
+      exceptions = emptyNode)
   result.typ = t
 
 proc createCastExpr(argsParam: PSym; objType: PType): PNode =
   result = newNodeI(nkCast, argsParam.info)
-  result.add emptyNode
+  result.add newNodeI(nkEmpty, argsParam.info)
   result.add newSymNode(argsParam)
   result.typ = newType(tyPtr, objType.owner)
   result.typ.rawAddSon(objType)
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index 460d0b4a5..d8fa4cedd 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -50,6 +50,7 @@ type
     exposed*: TStrTable
     intTypeCache*: array[-5..64, PType]
     opContains*, opNot*: PSym
+    emptyNode*: PNode
 
 proc hash*(x: FileIndex): Hash {.borrow.}
 
@@ -79,6 +80,7 @@ proc newModuleGraph*(config: ConfigRef = nil): ModuleGraph =
   initStrTable(result.exposed)
   result.opNot = createMagic(result, "not", mNot)
   result.opContains = createMagic(result, "contains", mInSet)
+  result.emptyNode = newNode(nkEmpty)
 
 proc resetAllModules*(g: ModuleGraph) =
   initStrTable(packageSyms)
diff --git a/compiler/parser.nim b/compiler/parser.nim
index fbc57ebb6..82e6549ed 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -40,6 +40,7 @@ type
     tok*: TToken               # The current token
     inPragma*: int             # Pragma level
     inSemiStmtList*: int
+    emptyNode: PNode
 
   SymbolMode = enum
     smNormal, smAllowNil, smAfterDot
@@ -93,6 +94,7 @@ proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream,
   getTok(p)                   # read the first token
   p.firstTok = true
   p.strongSpaces = strongSpaces
+  p.emptyNode = newNode(nkEmpty)
 
 proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
                  cache: IdentCache; config: ConfigRef;
@@ -333,7 +335,7 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
       getTok(p)
     else:
       parMessage(p, errIdentifierExpected, p.tok)
-      result = ast.emptyNode
+      result = p.emptyNode
   of tkAccent:
     result = newNodeP(nkAccQuoted, p)
     getTok(p)
@@ -364,7 +366,7 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
     # But: this really sucks for idetools and keywords, so we don't do it
     # if it is a keyword:
     #if not isKeyword(p.tok.tokType): getTok(p)
-    result = ast.emptyNode
+    result = p.emptyNode
 
 proc colonOrEquals(p: var TParser, a: PNode): PNode =
   if p.tok.tokType == tkColon:
@@ -703,7 +705,7 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode =
   else:
     parMessage(p, errExprExpected, p.tok)
     getTok(p)  # we must consume a token here to prevend endless loops!
-    result = ast.emptyNode
+    result = p.emptyNode
 
 proc namedParams(p: var TParser, callee: PNode,
                  kind: TNodeKind, endTok: TTokType): PNode =
@@ -1015,7 +1017,7 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
   #| paramListColon = paramList? (':' optInd typeDesc)?
   var a: PNode
   result = newNodeP(nkFormalParams, p)
-  addSon(result, ast.emptyNode) # return type
+  addSon(result, p.emptyNode) # return type
   let hasParLe = p.tok.tokType == tkParLe and p.tok.indent < 0
   if hasParLe:
     getTok(p)
@@ -1047,13 +1049,13 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
     result.sons[0] = parseTypeDesc(p)
   elif not retColon and not hasParle:
     # Mark as "not there" in order to mark for deprecation in the semantic pass:
-    result = ast.emptyNode
+    result = p.emptyNode
 
 proc optPragmas(p: var TParser): PNode =
   if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)):
     result = parsePragma(p)
   else:
-    result = ast.emptyNode
+    result = p.emptyNode
 
 proc parseDoBlock(p: var TParser; info: TLineInfo): PNode =
   #| doBlock = 'do' paramListArrow pragmas? colcom stmt
@@ -1062,7 +1064,9 @@ proc parseDoBlock(p: var TParser; info: TLineInfo): PNode =
   colcom(p, result)
   result = parseStmt(p)
   if params.kind != nkEmpty:
-    result = newProcNode(nkDo, info, result, params = params, pragmas = pragmas)
+    result = newProcNode(nkDo, info,
+      body = result, params = params, name = p.emptyNode, pattern = p.emptyNode,
+      genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode)
 
 proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode =
   #| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)?
@@ -1075,9 +1079,9 @@ proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode =
   if p.tok.tokType == tkEquals and isExpr:
     getTok(p)
     skipComment(p, result)
-    result = newProcNode(kind, info, parseStmt(p),
-                         params = params,
-                         pragmas = pragmas)
+    result = newProcNode(kind, info, body = parseStmt(p),
+      params = params, name = p.emptyNode, pattern = p.emptyNode,
+      genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode)
   else:
     result = newNodeI(nkProcTy, info)
     if hasSignature:
@@ -1244,8 +1248,8 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode =
   if p.tok.indent >= 0: return
 
   var
-    openingParams = emptyNode
-    openingPragmas = emptyNode
+    openingParams = p.emptyNode
+    openingPragmas = p.emptyNode
 
   if p.tok.tokType == tkDo:
     getTok(p)
@@ -1264,8 +1268,12 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode =
 
       stmtList.flags.incl nfBlockArg
       if openingParams.kind != nkEmpty:
-        result.add newProcNode(nkDo, stmtList.info, stmtList,
-                               params = openingParams, pragmas = openingPragmas)
+        result.add newProcNode(nkDo, stmtList.info, body = stmtList,
+                               params = openingParams,
+                               name = p.emptyNode, pattern = p.emptyNode,
+                               genericParams = p.emptyNode,
+                               pragmas = openingPragmas,
+                               exceptions = p.emptyNode)
       else:
         result.add stmtList
 
@@ -1424,10 +1432,10 @@ proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode =
   getTok(p)
   if p.tok.tokType == tkComment:
     skipComment(p, result)
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   elif p.tok.indent >= 0 and p.tok.indent <= p.currInd or not isExprStart(p):
     # NL terminates:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   else:
     var e = parseExpr(p)
     e = postExprBlocks(p, e)
@@ -1568,7 +1576,7 @@ proc parseBlock(p: var TParser): PNode =
   #| blockExpr = 'block' symbol? colcom stmt
   result = newNodeP(nkBlockStmt, p)
   getTokNoInd(p)
-  if p.tok.tokType == tkColon: addSon(result, ast.emptyNode)
+  if p.tok.tokType == tkColon: addSon(result, p.emptyNode)
   else: addSon(result, parseSymbol(p))
   colcom(p, result)
   addSon(result, parseStmt(p))
@@ -1586,7 +1594,7 @@ proc parseAsm(p: var TParser): PNode =
   result = newNodeP(nkAsmStmt, p)
   getTokNoInd(p)
   if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p))
-  else: addSon(result, ast.emptyNode)
+  else: addSon(result, p.emptyNode)
   case p.tok.tokType
   of tkStrLit: addSon(result, newStrNodeP(nkStrLit, p.tok.literal, p))
   of tkRStrLit: addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p))
@@ -1594,7 +1602,7 @@ proc parseAsm(p: var TParser): PNode =
                             newStrNodeP(nkTripleStrLit, p.tok.literal, p))
   else:
     parMessage(p, "the 'asm' statement takes a string literal")
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
     return
   getTok(p)
 
@@ -1625,13 +1633,13 @@ proc parseGenericParam(p: var TParser): PNode =
     optInd(p, result)
     addSon(result, parseExpr(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   if p.tok.tokType == tkEquals:
     getTok(p)
     optInd(p, result)
     addSon(result, parseExpr(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
 
 proc parseGenericParamList(p: var TParser): PNode =
   #| genericParamList = '[' optInd
@@ -1667,22 +1675,22 @@ proc parseRoutine(p: var TParser, kind: TNodeKind): PNode =
   optInd(p, result)
   addSon(result, identVis(p))
   if p.tok.tokType == tkCurlyLe and p.validInd: addSon(result, p.parsePattern)
-  else: addSon(result, ast.emptyNode)
+  else: addSon(result, p.emptyNode)
   if p.tok.tokType == tkBracketLe and p.validInd:
     result.add(p.parseGenericParamList)
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   addSon(result, p.parseParamList)
   if p.tok.tokType == tkCurlyDotLe and p.validInd: addSon(result, p.parsePragma)
-  else: addSon(result, ast.emptyNode)
+  else: addSon(result, p.emptyNode)
   # empty exception tracking:
-  addSon(result, ast.emptyNode)
+  addSon(result, p.emptyNode)
   if p.tok.tokType == tkEquals and p.validInd:
     getTok(p)
     skipComment(p, result)
     addSon(result, parseStmt(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   indAndComment(p, result)
 
 proc newCommentStmt(p: var TParser): PNode =
@@ -1732,7 +1740,7 @@ proc parseConstant(p: var TParser): PNode =
     optInd(p, result)
     addSon(result, parseTypeDesc(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   eat(p, tkEquals)
   optInd(p, result)
   addSon(result, parseExpr(p))
@@ -1742,7 +1750,7 @@ proc parseEnum(p: var TParser): PNode =
   #| enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+
   result = newNodeP(nkEnumTy, p)
   getTok(p)
-  addSon(result, ast.emptyNode)
+  addSon(result, p.emptyNode)
   optInd(p, result)
   flexComment(p, result)
   # progress guaranteed
@@ -1813,7 +1821,7 @@ proc parseObjectCase(p: var TParser): PNode =
   addSon(a, identWithPragma(p))
   eat(p, tkColon)
   addSon(a, parseTypeDesc(p))
-  addSon(a, ast.emptyNode)
+  addSon(a, p.emptyNode)
   addSon(result, a)
   if p.tok.tokType == tkColon: getTok(p)
   flexComment(p, result)
@@ -1872,7 +1880,7 @@ proc parseObjectPart(p: var TParser): PNode =
       result = newNodeP(nkNilLit, p)
       getTok(p)
     else:
-      result = ast.emptyNode
+      result = p.emptyNode
 
 proc parseObject(p: var TParser): PNode =
   #| object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart
@@ -1881,19 +1889,19 @@ proc parseObject(p: var TParser): PNode =
   if p.tok.tokType == tkCurlyDotLe and p.validInd:
     addSon(result, parsePragma(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   if p.tok.tokType == tkOf and p.tok.indent < 0:
     var a = newNodeP(nkOfInherit, p)
     getTok(p)
     addSon(a, parseTypeDesc(p))
     addSon(result, a)
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   if p.tok.tokType == tkComment:
     skipComment(p, result)
   # an initial IND{>} HAS to follow:
   if not realInd(p):
-    addSon(result, emptyNode)
+    addSon(result, p.emptyNode)
     return
   addSon(result, parseObjectPart(p))
 
@@ -1928,7 +1936,7 @@ proc parseTypeClass(p: var TParser): PNode =
   if p.tok.tokType == tkCurlyDotLe and p.validInd:
     addSon(result, parsePragma(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   if p.tok.tokType == tkOf and p.tok.indent < 0:
     var a = newNodeP(nkOfInherit, p)
     getTok(p)
@@ -1939,12 +1947,12 @@ proc parseTypeClass(p: var TParser): PNode =
       getTok(p)
     addSon(result, a)
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   if p.tok.tokType == tkComment:
     skipComment(p, result)
   # an initial IND{>} HAS to follow:
   if not realInd(p):
-    addSon(result, emptyNode)
+    addSon(result, p.emptyNode)
   else:
     addSon(result, parseStmt(p))
 
@@ -1957,14 +1965,14 @@ proc parseTypeDef(p: var TParser): PNode =
   if p.tok.tokType == tkBracketLe and p.validInd:
     addSon(result, parseGenericParamList(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   if p.tok.tokType == tkEquals:
     result.info = parLineInfo(p)
     getTok(p)
     optInd(p, result)
     addSon(result, parseTypeDefAux(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   indAndComment(p, result)    # special extension!
 
 proc parseVarTuple(p: var TParser): PNode =
@@ -1979,7 +1987,7 @@ proc parseVarTuple(p: var TParser): PNode =
     if p.tok.tokType != tkComma: break
     getTok(p)
     skipComment(p, a)
-  addSon(result, ast.emptyNode)         # no type desc
+  addSon(result, p.emptyNode)         # no type desc
   optPar(p)
   eat(p, tkParRi)
   eat(p, tkEquals)
@@ -2040,7 +2048,7 @@ proc simpleStmt(p: var TParser): PNode =
   of tkComment: result = newCommentStmt(p)
   else:
     if isExprStart(p): result = parseExprStmt(p)
-    else: result = ast.emptyNode
+    else: result = p.emptyNode
   if result.kind notin {nkEmpty, nkCommentStmt}: skipComment(p, result)
 
 proc complexOrSimpleStmt(p: var TParser): PNode =
@@ -2136,7 +2144,7 @@ proc parseStmt(p: var TParser): PNode =
     of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkFunc,
        tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar:
       parMessage(p, "complex statement requires indentation")
-      result = ast.emptyNode
+      result = p.emptyNode
     else:
       if p.inSemiStmtList > 0:
         result = simpleStmt(p)
@@ -2173,7 +2181,7 @@ proc parseAll(p: var TParser): PNode =
 proc parseTopLevelStmt(p: var TParser): PNode =
   ## Implements an iterator which, when called repeatedly, returns the next
   ## top-level statement or emptyNode if end of stream.
-  result = ast.emptyNode
+  result = p.emptyNode
   # progress guaranteed
   while true:
     if p.tok.indent != 0:
diff --git a/compiler/plugins/itersgen.nim b/compiler/plugins/itersgen.nim
index ebb65dd4a..7af97904e 100644
--- a/compiler/plugins/itersgen.nim
+++ b/compiler/plugins/itersgen.nim
@@ -40,10 +40,10 @@ proc iterToProcImpl(c: PContext, n: PNode): PNode =
   prc.typ.rawAddSon t
   let orig = iter.sym.ast
   prc.ast = newProcNode(nkProcDef, n.info,
-                        name = newSymNode(prc),
-                        params = orig[paramsPos],
-                        pragmas = orig[pragmasPos],
-                        body = body)
+              body = body, params = orig[paramsPos], name = newSymNode(prc),
+              pattern = c.graph.emptyNode, genericParams = c.graph.emptyNode,
+              pragmas = orig[pragmasPos], exceptions = c.graph.emptyNode)
+
   prc.ast.add iter.sym.ast.sons[resultPos]
   addInterfaceDecl(c, prc)
 
diff --git a/compiler/rodread.nim b/compiler/rodread.nim
index 52e7a924c..86cac0d23 100644
--- a/compiler/rodread.nim
+++ b/compiler/rodread.nim
@@ -966,7 +966,7 @@ proc getBody*(s: PSym): PNode =
   ## accessor.
   assert s.kind in routineKinds
   # prevent crashes due to incorrect macro transformations (bug #2377)
-  if s.ast.isNil or bodyPos >= s.ast.len: return ast.emptyNode
+  if s.ast.isNil or bodyPos >= s.ast.len: return newNodeI(nkEmpty, s.info)
   result = s.ast.sons[bodyPos]
   if result == nil:
     assert s.offset != 0
diff --git a/compiler/sem.nim b/compiler/sem.nim
index c5c3dd99b..f3cc6288d 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -594,7 +594,7 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
         c.suggestionsMade = true
         result = nil
       else:
-        result = ast.emptyNode
+        result = newNodeI(nkEmpty, n.info)
       #if c.config.cmd == cmdIdeTools: findSuggest(c, n)
   rod.storeNode(c.module, result)
 
diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim
index f0fde195c..4a923ef2b 100644
--- a/compiler/semasgn.nim
+++ b/compiler/semasgn.nim
@@ -157,7 +157,7 @@ proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
 proc addVar(father, v, value: PNode) =
   var vpart = newNodeI(nkIdentDefs, v.info, 3)
   vpart.sons[0] = v
-  vpart.sons[1] = ast.emptyNode
+  vpart.sons[1] = newNodeI(nkEmpty, v.info)
   vpart.sons[2] = value
   addSon(father, vpart)
 
@@ -297,7 +297,7 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
   of attachedDestructor: typ.destructor = result
 
   var n = newNodeI(nkProcDef, info, bodyPos+1)
-  for i in 0 ..< n.len: n.sons[i] = emptyNode
+  for i in 0 ..< n.len: n.sons[i] = newNodeI(nkEmpty, info)
   n.sons[namePos] = newSymNode(result)
   n.sons[paramsPos] = result.typ.n
   n.sons[bodyPos] = body
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 6b32fa66c..aa98bf930 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -634,7 +634,7 @@ proc semStaticExpr(c: PContext, n: PNode): PNode =
   result = evalStaticExpr(c.module, c.cache, c.graph, a, c.p.owner)
   if result.isNil:
     localError(c.config, n.info, errCannotInterpretNodeX % renderTree(n))
-    result = emptyNode
+    result = c.graph.emptyNode
   else:
     result = fixupTypeAfterEval(c, result, a)
 
@@ -745,7 +745,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
       if errorOutputs == {}:
         # speed up error generation:
         globalError(c.config, n.info, "type mismatch")
-        return emptyNode
+        return c.graph.emptyNode
       else:
         var hasErrorType = false
         var msg = "type mismatch: got <"
@@ -866,7 +866,7 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
         else:
           if check == nil:
             check = newNodeI(nkCheckedFieldExpr, n.info)
-            addSon(check, ast.emptyNode) # make space for access node
+            addSon(check, c.graph.emptyNode) # make space for access node
           s = newNodeIT(nkCurly, n.info, setType)
           for j in countup(0, sonsLen(it) - 2): addSon(s, copyTree(it.sons[j]))
           var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool))
@@ -881,7 +881,7 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
         if result != nil:
           if check == nil:
             check = newNodeI(nkCheckedFieldExpr, n.info)
-            addSon(check, ast.emptyNode) # make space for access node
+            addSon(check, c.graph.emptyNode) # make space for access node
           var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool))
           addSon(inExpr, newSymNode(c.graph.opContains, n.info))
           addSon(inExpr, s)
@@ -938,7 +938,7 @@ proc readTypeParameter(c: PContext, typ: PType,
           if rawTyp.n != nil:
             return rawTyp.n
           else:
-            return emptyNode
+            return c.graph.emptyNode
         else:
           let foundTyp = makeTypeDesc(c, rawTyp)
           return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info)
@@ -1106,7 +1106,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
     case t.kind
     of tyTypeParamsHolders:
       result = readTypeParameter(c, t, i, n.info)
-      if result == emptyNode:
+      if result == c.graph.emptyNode:
         result = n
         n.typ = makeTypeFromExpr(c, n.copyTree)
       return
@@ -1489,7 +1489,7 @@ proc semReturn(c: PContext, n: PNode): PNode =
         n.sons[0] = semAsgn(c, a)
         # optimize away ``result = result``:
         if n[0][1].kind == nkSym and n[0][1].sym == c.p.resultSym:
-          n.sons[0] = ast.emptyNode
+          n.sons[0] = c.graph.emptyNode
       else:
         localError(c.config, n.info, errNoReturnTypeDeclared)
   else:
@@ -1748,14 +1748,17 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
   processQuotations(c, quotedBlock, op, quotes, ids)
 
   var dummyTemplate = newProcNode(
-    nkTemplateDef, quotedBlock.info, quotedBlock,
-    name = newAnonSym(c, skTemplate, n.info).newSymNode)
+    nkTemplateDef, quotedBlock.info, body = quotedBlock,
+    params = c.graph.emptyNode,
+    name = newAnonSym(c, skTemplate, n.info).newSymNode,
+              pattern = c.graph.emptyNode, genericParams = c.graph.emptyNode,
+              pragmas = c.graph.emptyNode, exceptions = c.graph.emptyNode)
 
   if ids.len > 0:
     dummyTemplate.sons[paramsPos] = newNodeI(nkFormalParams, n.info)
     dummyTemplate[paramsPos].add getSysSym(c.graph, n.info, "typed").newSymNode # return type
     ids.add getSysSym(c.graph, n.info, "untyped").newSymNode # params type
-    ids.add emptyNode # no default value
+    ids.add c.graph.emptyNode # no default value
     dummyTemplate[paramsPos].add newNode(nkIdentDefs, n.info, ids)
 
   var tmpl = semTemplateDef(c, dummyTemplate)
@@ -1913,7 +1916,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
         result.typ = typ
       result.add instantiateCreateFlowVarCall(c, typ, n.info).newSymNode
     else:
-      result.add emptyNode
+      result.add c.graph.emptyNode
   of mProcCall:
     result = setMs(n, s)
     result.sons[1] = semExpr(c, n.sons[1])
@@ -1944,10 +1947,10 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
         let imports = newTree(nkStmtList)
         extractImports(n.lastSon, imports)
         for imp in imports: c.runnableExamples.add imp
-        c.runnableExamples.add newTree(nkBlockStmt, emptyNode, copyTree n.lastSon)
+        c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree n.lastSon)
       result = setMs(n, s)
     else:
-      result = emptyNode
+      result = c.graph.emptyNode
   else:
     result = semDirectOp(c, n, flags)
 
diff --git a/compiler/semfields.nim b/compiler/semfields.nim
index 16d79c559..b432ee963 100644
--- a/compiler/semfields.nim
+++ b/compiler/semfields.nim
@@ -162,7 +162,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
   # we avoid it now if we can:
   if containsNode(stmts, {nkBreakStmt}):
     var b = newNodeI(nkBreakStmt, n.info)
-    b.add(ast.emptyNode)
+    b.add(newNodeI(nkEmpty, n.info))
     stmts.add(b)
   else:
     result = stmts
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index a5f0ca7d8..e95b94553 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -328,7 +328,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
     inc i
   if tfTriggersCompileTime in result.typ.flags:
     incl(result.flags, sfCompileTime)
-  n.sons[genericParamsPos] = ast.emptyNode
+  n.sons[genericParamsPos] = c.graph.emptyNode
   var oldPrc = genericCacheGet(fn, entry[], c.compilesContextId)
   if oldPrc == nil:
     # we MUST not add potentially wrong instantiations to the caching mechanism.
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 8515e971d..91bfc0b1d 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -154,7 +154,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
     result = newIntNodeT(ord(not complexObj), traitCall, c.graph)
   else:
     localError(c.config, traitCall.info, "unknown trait")
-    result = emptyNode
+    result = newNodeI(nkEmpty, traitCall.info)
 
 proc semTypeTraits(c: PContext, n: PNode): PNode =
   checkMinSonsLen(n, 2, c.config)
diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim
index ea5aab628..9e7aead13 100644
--- a/compiler/semparallel.nim
+++ b/compiler/semparallel.nim
@@ -438,7 +438,7 @@ proc transformSpawn(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode =
         let t = b[1][0].typ.sons[0]
         if spawnResult(t, true) == srByVar:
           result.add wrapProcForSpawn(g, owner, m, b.typ, barrier, it[0])
-          it.sons[it.len-1] = emptyNode
+          it.sons[it.len-1] = newNodeI(nkEmpty, it.info)
         else:
           it.sons[it.len-1] = wrapProcForSpawn(g, owner, m, b.typ, barrier, nil)
     if result.isNil: result = n
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index b66d7d9f2..2ba2a6ace 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -915,8 +915,8 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) =
   newSeq(effects.sons, effectListLen)
   effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
   effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
-  effects.sons[usesEffects] = ast.emptyNode
-  effects.sons[writeEffects] = ast.emptyNode
+  effects.sons[usesEffects] = g.emptyNode
+  effects.sons[writeEffects] = g.emptyNode
 
   t.exc = effects.sons[exceptionEffects]
   t.tags = effects.sons[tagEffects]
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 3687e50e9..fe1afb709 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -394,7 +394,7 @@ proc isDiscardUnderscore(v: PSym): bool =
     result = true
 
 proc semUsing(c: PContext; n: PNode): PNode =
-  result = ast.emptyNode
+  result = c.graph.emptyNode
   if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "using")
   for i in countup(0, sonsLen(n)-1):
     var a = n.sons[i]
@@ -481,7 +481,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
       typ = semTypeNode(c, a.sons[length-2], nil)
     else:
       typ = nil
-    var def: PNode = ast.emptyNode
+    var def: PNode = c.graph.emptyNode
     if a.sons[length-1].kind != nkEmpty:
       def = semExprWithType(c, a.sons[length-1], {efAllowDestructor})
       if def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
@@ -1176,7 +1176,7 @@ proc semProcAnnotation(c: PContext, prc: PNode;
     x.add(newSymNode(m))
     prc.sons[pragmasPos] = copyExcept(n, i)
     if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0:
-      prc.sons[pragmasPos] = emptyNode
+      prc.sons[pragmasPos] = c.graph.emptyNode
 
     if it.kind in nkPragmaCallKinds and it.len > 1:
       # pass pragma arguments to the macro too:
@@ -1199,7 +1199,7 @@ proc setGenericParamsMisc(c: PContext; n: PNode): PNode =
   # issue https://github.com/nim-lang/Nim/issues/1713
   result = semGenericParamList(c, orig)
   if n.sons[miscPos].kind == nkEmpty:
-    n.sons[miscPos] = newTree(nkBracket, ast.emptyNode, orig)
+    n.sons[miscPos] = newTree(nkBracket, c.graph.emptyNode, orig)
   else:
     n.sons[miscPos].sons[1] = orig
   n.sons[genericParamsPos] = result
@@ -1270,7 +1270,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
   result = n
   s.ast = result
   n.sons[namePos].sym = s
-  n.sons[genericParamsPos] = emptyNode
+  n.sons[genericParamsPos] = c.graph.emptyNode
   # for LL we need to avoid wrong aliasing
   let params = copyTree n.typ.n
   n.sons[paramsPos] = params
@@ -1613,7 +1613,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
         if s.kind == skMethod: semMethodPrototype(c, s, n)
       if sfImportc in s.flags:
         # so we just ignore the body after semantic checking for importc:
-        n.sons[bodyPos] = ast.emptyNode
+        n.sons[bodyPos] = c.graph.emptyNode
       popProcCon(c)
   else:
     if s.kind == skMethod: semMethodPrototype(c, s, n)
@@ -1692,7 +1692,7 @@ proc semMethod(c: PContext, n: PNode): PNode =
     let ret = s.typ.sons[0]
     disp.typ.sons[0] = ret
     if disp.ast[resultPos].kind == nkSym:
-      if isEmptyType(ret): disp.ast.sons[resultPos] = emptyNode
+      if isEmptyType(ret): disp.ast.sons[resultPos] = c.graph.emptyNode
       else: disp.ast[resultPos].sym.typ = ret
 
 proc semConverterDef(c: PContext, n: PNode): PNode =
@@ -1771,7 +1771,7 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
   n.sons[0] = a
   evalStaticStmt(c.module, c.cache, c.graph, a, c.p.owner)
   result = newNodeI(nkDiscardStmt, n.info, 1)
-  result.sons[0] = emptyNode
+  result.sons[0] = c.graph.emptyNode
 
 proc usesResult(n: PNode): bool =
   # nkStmtList(expr) properly propagates the void context,
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 8b5c26f99..69f1be0a1 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -658,7 +658,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
     var length = sonsLen(n)
     var a: PNode
     if father.kind != nkRecList and length>=4: a = newNodeI(nkRecList, n.info)
-    else: a = ast.emptyNode
+    else: a = newNodeI(nkEmpty, n.info)
     if n.sons[length-1].kind != nkEmpty:
       localError(c.config, n.sons[length-1].info, errInitHereNotAllowed)
     var typ: PType
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 41cac2a4a..0df52d0af 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -1774,7 +1774,7 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
   else:
     result.typ = f
   if result.typ == nil: internalError(c.graph.config, arg.info, "implicitConv")
-  addSon(result, ast.emptyNode)
+  addSon(result, c.graph.emptyNode)
   addSon(result, arg)
 
 proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
@@ -1993,7 +1993,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
       return arg
     elif a.kind == tyVoid and f.matchesVoidProc and argOrig.kind == nkStmtList:
       # lift do blocks without params to lambdas
-      let lifted = c.semExpr(c, newProcNode(nkDo, argOrig.info, argOrig), {})
+      let p = c.graph
+      let lifted = c.semExpr(c, newProcNode(nkDo, argOrig.info, body = argOrig,
+          params = p.emptyNode, name = p.emptyNode, pattern = p.emptyNode,
+          genericParams = p.emptyNode, pragmas = p.emptyNode, exceptions = p.emptyNode), {})
       if f.kind == tyBuiltInTypeClass:
         inc m.genericMatches
         put(m, f, lifted.typ)
@@ -2354,7 +2357,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
 proc argtypeMatches*(c: PContext, f, a: PType): bool =
   var m: TCandidate
   initCandidate(c, m, f)
-  let res = paramTypesMatch(m, f, a, ast.emptyNode, nil)
+  let res = paramTypesMatch(m, f, a, c.graph.emptyNode, nil)
   #instantiateGenericConverters(c, res, m)
   # XXX this is used by patterns.nim too; I think it's better to not
   # instantiate generic converters for that
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 23aecfa71..7d4eae380 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -510,7 +510,7 @@ proc safeSemExpr*(c: PContext, n: PNode): PNode =
   try:
     result = c.semExpr(c, n)
   except ERecoverableError:
-    result = ast.emptyNode
+    result = c.graph.emptyNode
 
 proc sugExpr(c: PContext, n: PNode, outputs: var Suggestions) =
   if n.kind == nkDotExpr:
diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim
index 4bc153e46..109f90cb1 100644
--- a/compiler/syntaxes.nim
+++ b/compiler/syntaxes.nim
@@ -38,7 +38,6 @@ proc parseAll*(p: var TParsers): PNode =
     result = parser.parseAll(p.parser)
   of skinEndX:
     internalError(p.config, "parser to implement")
-    result = ast.emptyNode
 
 proc parseTopLevelStmt*(p: var TParsers): PNode =
   case p.skin
@@ -46,7 +45,6 @@ proc parseTopLevelStmt*(p: var TParsers): PNode =
     result = parser.parseTopLevelStmt(p.parser)
   of skinEndX:
     internalError(p.config, "parser to implement")
-    result = ast.emptyNode
 
 proc utf8Bom(s: string): int =
   if s.len >= 3 and s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF':
@@ -62,7 +60,7 @@ proc containsShebang(s: string, i: int): bool =
 
 proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache;
                config: ConfigRef): PNode =
-  result = ast.emptyNode
+  result = newNode(nkEmpty)
   var s = llStreamOpen(filename, fmRead)
   if s != nil:
     var line = newStringOfCap(80)
diff --git a/compiler/transf.nim b/compiler/transf.nim
index a10e8a1e5..81085af96 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -194,7 +194,7 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
         idNodeTablePut(c.transCon.mapping, it.sons[j].sym, x)
         defs[j] = x.PTransNode
       assert(it.sons[L-2].kind == nkEmpty)
-      defs[L-2] = ast.emptyNode.PTransNode
+      defs[L-2] = newNodeI(nkEmpty, it.info).PTransNode
       defs[L-1] = transform(c, it.sons[L-1])
       result[i] = defs
 
@@ -393,7 +393,7 @@ proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode =
   if c.graph.config.cmd == cmdCompileToJS: return prc
   result = newNodeIT(nkClosure, prc.info, dest)
   var conv = newNodeIT(nkHiddenSubConv, prc.info, dest)
-  conv.add(emptyNode)
+  conv.add(newNodeI(nkEmpty, prc.info))
   conv.add(prc)
   if prc.kind == nkClosure:
     internalError(c.graph.config, prc.info, "closure to closure created")
@@ -721,16 +721,16 @@ proc transformExceptBranch(c: PTransf, n: PNode): PTransNode =
     let actions = newTransNode(nkStmtListExpr, n[1], 2)
     # Generating `let exc = (excType)(getCurrentException())`
     # -> getCurrentException()
-    let excCall = PTransNode(callCodegenProc(c.graph, "getCurrentException", ast.emptyNode))
+    let excCall = PTransNode(callCodegenProc(c.graph, "getCurrentException", newNodeI(nkEmpty, n.info)))
     # -> (excType)
     let convNode = newTransNode(nkHiddenSubConv, n[1].info, 2)
-    convNode[0] = PTransNode(ast.emptyNode)
+    convNode[0] = PTransNode(newNodeI(nkEmpty, n.info))
     convNode[1] = excCall
     PNode(convNode).typ = excTypeNode.typ.toRef()
     # -> let exc = ...
     let identDefs = newTransNode(nkIdentDefs, n[1].info, 3)
     identDefs[0] = PTransNode(n[0][2])
-    identDefs[1] = PTransNode(ast.emptyNode)
+    identDefs[1] = PTransNode(newNodeI(nkEmpty, n.info))
     identDefs[2] = convNode
 
     let letSection = newTransNode(nkLetSection, n[1].info, 1)
diff --git a/compiler/vm.nim b/compiler/vm.nim
index dcb4cefd7..5eb4d70f3 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -213,7 +213,7 @@ proc putIntoNode(n: var PNode; x: TFullReg) =
     if nfIsRef in x.node.flags:
       n = x.node
     else:
-      let destIsRef = nfIsRef in n.flags    
+      let destIsRef = nfIsRef in n.flags
       n[] = x.node[]
       # Ref-ness must be kept for the destination
       if destIsRef:
@@ -1683,7 +1683,7 @@ proc myProcess(c: PPassContext, n: PNode): PNode =
   # don't eval errornous code:
   if c.oldErrorCount == c.config.errorCounter:
     evalStmt(c, n)
-    result = emptyNode
+    result = newNodeI(nkEmpty, n.info)
   else:
     result = n
   c.oldErrorCount = c.config.errorCounter
@@ -1703,7 +1703,7 @@ proc evalConstExprAux(module: PSym; cache: IdentCache;
   defer: c.mode = oldMode
   c.mode = mode
   let start = genExpr(c, n, requiresValue = mode!=emStaticStmt)
-  if c.code[start].opcode == opcEof: return emptyNode
+  if c.code[start].opcode == opcEof: return newNodeI(nkEmpty, n.info)
   assert c.code[start].opcode != opcEof
   when debugEchoCode: c.echoCode start
   var tos = PStackFrame(prc: prc, comesFrom: 0, next: nil)
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index 2c92348a6..5f3cc2d49 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -54,7 +54,7 @@ proc objectNode(n: PNode): PNode =
     result = newNodeI(nkIdentDefs, n.info)
     result.add n  # name
     result.add mapTypeToAstX(n.sym.typ, n.info, true, false)  # type
-    result.add ast.emptyNode  # no assigned value
+    result.add newNodeI(nkEmpty, n.info)  # no assigned value
   else:
     result = copyNode(n)
     for i in 0 ..< n.safeLen:
@@ -69,7 +69,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
   template mapTypeToAstR(t,info): untyped = mapTypeToAstX(t, info, inst, true)
   template mapTypeToAst(t,i,info): untyped =
     if i<t.len and t.sons[i]!=nil: mapTypeToAstX(t.sons[i], info, inst)
-    else: ast.emptyNode
+    else: newNodeI(nkEmpty, info)
   template mapTypeToBracket(name, m, t, info): untyped =
     mapTypeToBracketX(name, m, t, info, inst)
   template newNodeX(kind): untyped =
@@ -78,7 +78,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
     var id = newNodeX(nkIdentDefs)
     id.add n  # name
     id.add mapTypeToAst(t, info)  # type
-    id.add ast.emptyNode  # no assigned value
+    id.add newNodeI(nkEmpty, info)  # no assigned value
     id
   template newIdentDefs(s): untyped = newIdentDefs(s, s.typ)
 
@@ -156,8 +156,8 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
   of tyObject:
     if inst:
       result = newNodeX(nkObjectTy)
-      result.add ast.emptyNode  # pragmas not reconstructed yet
-      if t.sons[0] == nil: result.add ast.emptyNode  # handle parent object
+      result.add newNodeI(nkEmpty, info)  # pragmas not reconstructed yet
+      if t.sons[0] == nil: result.add newNodeI(nkEmpty, info)  # handle parent object
       else:
         var nn = newNodeX(nkOfInherit)
         nn.add mapTypeToAst(t.sons[0], info)
@@ -165,13 +165,13 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
       if t.n.len > 0:
         result.add objectNode(t.n)
       else:
-        result.add ast.emptyNode
+        result.add newNodeI(nkEmpty, info)
     else:
       if allowRecursion or t.sym == nil:
         result = newNodeIT(nkObjectTy, if t.n.isNil: info else: t.n.info, t)
-        result.add ast.emptyNode
+        result.add newNodeI(nkEmpty, info)
         if t.sons[0] == nil:
-          result.add ast.emptyNode
+          result.add newNodeI(nkEmpty, info)
         else:
           result.add mapTypeToAst(t.sons[0], info)
         result.add copyTree(t.n)
@@ -179,7 +179,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
         result = atomicType(t.sym)
   of tyEnum:
     result = newNodeIT(nkEnumTy, if t.n.isNil: info else: t.n.info, t)
-    result.add ast.emptyNode  # pragma node, currently always empty for enum
+    result.add newNodeI(nkEmpty, info)  # pragma node, currently always empty for enum
     for c in t.n.sons:
       result.add copyTree(c)
   of tyTuple:
@@ -223,13 +223,13 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
       result = newNodeX(nkProcTy)
       var fp = newNodeX(nkFormalParams)
       if t.sons[0] == nil:
-        fp.add ast.emptyNode
+        fp.add newNodeI(nkEmpty, info)
       else:
         fp.add mapTypeToAst(t.sons[0], t.n[0].info)
       for i in 1..<t.sons.len:
         fp.add newIdentDefs(t.n[i], t.sons[i])
       result.add fp
-      result.add ast.emptyNode  # pragmas aren't reconstructed yet
+      result.add newNodeI(nkEmpty, info)  # pragmas aren't reconstructed yet
     else:
       result = mapTypeToBracket("proc", mNone, t, info)
   of tyOpenArray: result = mapTypeToBracket("openArray", mOpenArray, t, info)
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 7ac3b5cf7..177ddc1b9 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -1543,7 +1543,6 @@ proc getNullValueAux(obj: PNode, result: PNode; conf: ConfigRef) =
 
 proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
   var t = skipTypes(typ, abstractRange-{tyTypeDesc})
-  result = emptyNode
   case t.kind
   of tyBool, tyEnum, tyChar, tyInt..tyInt64:
     result = newNodeIT(nkIntLit, info, t)
@@ -1589,6 +1588,7 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
     result = newNodeIT(nkBracket, info, t)
   else:
     globalError(conf, info, "cannot create null element for: " & $t.kind)
+    result = newNodeI(nkEmpty, info)
 
 proc ldNullOpcode(t: PType): TOpcode =
   assert t != nil
@@ -2008,7 +2008,7 @@ proc genProc(c: PCtx; s: PSym): int =
     #c.removeLastEof
     result = c.code.len+1 # skip the jump instruction
     if x.kind == nkEmpty:
-      x = newTree(nkBracket, newIntNode(nkIntLit, result), ast.emptyNode)
+      x = newTree(nkBracket, newIntNode(nkIntLit, result), x)
     else:
       x.sons[0] = newIntNode(nkIntLit, result)
     s.ast.sons[miscPos] = x