summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim68
-rw-r--r--compiler/astyaml.nim2
-rw-r--r--compiler/backendpragmas.nim25
-rw-r--r--compiler/cbuilder.nim174
-rw-r--r--compiler/ccgcalls.nim120
-rw-r--r--compiler/ccgexprs.nim257
-rw-r--r--compiler/ccgreset.nim16
-rw-r--r--compiler/ccgstmts.nim79
-rw-r--r--compiler/ccgthreadvars.nim4
-rw-r--r--compiler/ccgtrav.nim18
-rw-r--r--compiler/ccgtypes.nim265
-rw-r--r--compiler/ccgutils.nim5
-rw-r--r--compiler/cgen.nim267
-rw-r--r--compiler/cgendata.nim2
-rw-r--r--compiler/cgmeth.nim2
-rw-r--r--compiler/closureiters.nim201
-rw-r--r--compiler/commands.nim11
-rw-r--r--compiler/condsyms.nim3
-rw-r--r--compiler/dfa.nim16
-rw-r--r--compiler/docgen.nim8
-rw-r--r--compiler/evaltempl.nim14
-rw-r--r--compiler/expanddefaults.nim2
-rw-r--r--compiler/extccomp.nim31
-rw-r--r--compiler/ic/ic.nim85
-rw-r--r--compiler/ic/iclineinfos.nim (renamed from compiler/nir/nirlineinfos.nim)2
-rw-r--r--compiler/ic/integrity.nim10
-rw-r--r--compiler/ic/navigator.nim4
-rw-r--r--compiler/ic/packed_ast.nim4
-rw-r--r--compiler/ic/rodfiles.nim27
-rw-r--r--compiler/importer.nim3
-rw-r--r--compiler/injectdestructors.nim40
-rw-r--r--compiler/installer.ini4
-rw-r--r--compiler/jsgen.nim251
-rw-r--r--compiler/lambdalifting.nim61
-rw-r--r--compiler/liftdestructors.nim36
-rw-r--r--compiler/lineinfos.nim7
-rw-r--r--compiler/llstream.nim1
-rw-r--r--compiler/lookups.nim62
-rw-r--r--compiler/main.nim39
-rw-r--r--compiler/modulegraphs.nim73
-rw-r--r--compiler/msgs.nim38
-rw-r--r--compiler/ndi.nim2
-rw-r--r--compiler/nim.nim6
-rw-r--r--compiler/nir/ast2ir.nim2638
-rw-r--r--compiler/nir/cir.nim983
-rw-r--r--compiler/nir/nir.nim105
-rw-r--r--compiler/nir/nirc.nim52
-rw-r--r--compiler/nir/nirfiles.nim83
-rw-r--r--compiler/nir/nirinsts.nim582
-rw-r--r--compiler/nir/nirslots.nim104
-rw-r--r--compiler/nir/nirtypes.nim475
-rw-r--r--compiler/nir/nirvm.nim1175
-rw-r--r--compiler/nir/stringcases.nim200
-rw-r--r--compiler/nir/types2ir.nim525
-rw-r--r--compiler/nodekinds.nim1
-rw-r--r--compiler/options.nim35
-rw-r--r--compiler/parampatterns.nim13
-rw-r--r--compiler/parser.nim2
-rw-r--r--compiler/pipelines.nim13
-rw-r--r--compiler/pragmas.nim56
-rw-r--r--compiler/pushpoppragmas.nim54
-rw-r--r--compiler/renderer.nim9
-rw-r--r--compiler/reorder.nim26
-rw-r--r--compiler/sem.nim83
-rw-r--r--compiler/semcall.nim173
-rw-r--r--compiler/semdata.nim6
-rw-r--r--compiler/semexprs.nim442
-rw-r--r--compiler/semfold.nim19
-rw-r--r--compiler/semgnrc.nim101
-rw-r--r--compiler/seminst.nim47
-rw-r--r--compiler/semmacrosanity.nim1
-rw-r--r--compiler/semmagic.nim22
-rw-r--r--compiler/semobjconstr.nim11
-rw-r--r--compiler/sempass2.nim16
-rw-r--r--compiler/semstmts.nim238
-rw-r--r--compiler/semtempl.nim102
-rw-r--r--compiler/semtypes.nim192
-rw-r--r--compiler/semtypinst.nim123
-rw-r--r--compiler/sighashes.nim8
-rw-r--r--compiler/sigmatch.nim490
-rw-r--r--compiler/sizealignoffsetimpl.nim7
-rw-r--r--compiler/spawn.nim16
-rw-r--r--compiler/suggest.nim5
-rw-r--r--compiler/transf.nim53
-rw-r--r--compiler/typeallowed.nim11
-rw-r--r--compiler/types.nim173
-rw-r--r--compiler/varpartitions.nim30
-rw-r--r--compiler/vm.nim89
-rw-r--r--compiler/vmdeps.nim15
-rw-r--r--compiler/vmgen.nim143
-rw-r--r--compiler/vmops.nim5
91 files changed, 3473 insertions, 8624 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index e8fee6c0d..a342e1ea7 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -41,7 +41,7 @@ type
   TNodeKinds* = set[TNodeKind]
 
 type
-  TSymFlag* = enum    # 51 flags!
+  TSymFlag* = enum    # 63 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
@@ -93,7 +93,7 @@ type
     sfNamedParamCall, # symbol needs named parameter call syntax in target
                       # language; for interfacing with Objective C
     sfDiscardable,    # returned value may be discarded implicitly
-    sfOverridden,      # proc is overridden
+    sfOverridden,     # proc is overridden
     sfCallsite        # A flag for template symbols to tell the
                       # compiler it should use line information from
                       # the calling side of the macro, not from the
@@ -126,24 +126,25 @@ type
     sfByCopy          # param is marked as pass bycopy
     sfMember          # proc is a C++ member of a type
     sfCodegenDecl     # type, proc, global or proc param is marked as codegenDecl
+    sfWasGenSym       # symbol was 'gensym'ed
+    sfForceLift       # variable has to be lifted into closure environment
+
+    sfDirty           # template is not hygienic (old styled template) module,
+                      # compiled from a dirty-buffer
+    sfCustomPragma    # symbol is custom pragma template
+    sfBase,           # a base method
+    sfGoto            # var is used for 'goto' code generation
+    sfAnon,           # symbol name that was generated by the compiler
+                      # the compiler will avoid printing such names
+                      # in user messages.
+    sfAllUntyped      # macro or template is immediately expanded in a generic context
+    sfTemplateRedefinition # symbol is a redefinition of an earlier template
 
   TSymFlags* = set[TSymFlag]
 
 const
   sfNoInit* = sfMainModule       # don't generate code to init the variable
 
-  sfAllUntyped* = sfVolatile # macro or template is immediately expanded \
-    # in a generic context
-
-  sfDirty* = sfPure
-    # template is not hygienic (old styled template)
-    # module, compiled from a dirty-buffer
-
-  sfAnon* = sfDiscardable
-    # symbol name that was generated by the compiler
-    # the compiler will avoid printing such names
-    # in user messages.
-
   sfNoForward* = sfRegister
     # forward declarations are not required (per module)
   sfReorder* = sfForward
@@ -152,12 +153,8 @@ const
   sfCompileToCpp* = sfInfixCall       # compile the module as C++ code
   sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
   sfExperimental* = sfOverridden       # module uses the .experimental switch
-  sfGoto* = sfOverridden               # var is used for 'goto' code generation
   sfWrittenTo* = sfBorrow             # param is assigned to
                                       # currently unimplemented
-  sfBase* = sfDiscriminant
-  sfCustomPragma* = sfRegister        # symbol is custom pragma template
-  sfTemplateRedefinition* = sfExportc # symbol is a redefinition of an earlier template
   sfCppMember* = { sfVirtual, sfMember, sfConstructor } # proc is a C++ member, meaning it will be attached to the type definition
 
 const
@@ -217,7 +214,8 @@ type
     tyUncheckedArray
       # An array with boundaries [0,+∞]
 
-    tyProxy # used as errornous type (for idetools)
+    tyError # used as erroneous type (for idetools)
+      # as an erroneous node should match everything
 
     tyBuiltInTypeClass
       # Type such as the catch-all object, tuple, seq, etc
@@ -279,10 +277,6 @@ static:
 const
   tyPureObject* = tyTuple
   GcTypeKinds* = {tyRef, tySequence, tyString}
-  tyError* = tyProxy # as an errornous node should match everything
-  tyUnknown* = tyFromExpr
-
-  tyUnknownTypes* = {tyError, tyFromExpr}
 
   tyTypeClasses* = {tyBuiltInTypeClass, tyCompositeTypeClass,
                     tyUserTypeClass, tyUserTypeClassInst,
@@ -328,7 +322,9 @@ type
     nfFirstWrite # this node is a first write
     nfHasComment # node has a comment
     nfSkipFieldChecking # node skips field visable checking
-    nfOpenSym # node is a captured sym but can be overriden by local symbols
+    nfDisabledOpenSym # temporary: node should be nkOpenSym but cannot
+                      # because openSym experimental switch is disabled
+                      # gives warning instead
 
   TNodeFlags* = set[TNodeFlag]
   TTypeFlag* = enum   # keep below 32 for efficiency reasons (now: 47)
@@ -449,6 +445,8 @@ const
   tfGcSafe* = tfThread
   tfObjHasKids* = tfEnumHasHoles
   tfReturnsNew* = tfInheritable
+  tfNonConstExpr* = tfExplicitCallConv
+    ## tyFromExpr where the expression shouldn't be evaluated as a static value
   skError* = skUnknown
 
 var
@@ -504,7 +502,7 @@ type
     mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq,
     mNewString, mNewStringOfCap, mParseBiggestFloat,
     mMove, mEnsureMove, mWasMoved, mDup, mDestroy, mTrace,
-    mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mReset,
+    mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField,
     mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
     mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
     mOrdinal, mIterableType,
@@ -654,7 +652,7 @@ type
     storage*: TStorageLoc
     flags*: TLocFlags         # location's flags
     lode*: PNode              # Node where the location came from; can be faked
-    r*: Rope                  # rope value of location (code generators)
+    snippet*: Rope            # C code snippet of location (code generators)
 
   # ---------------- end of backend information ------------------------------
 
@@ -883,7 +881,7 @@ const
                                       nfFromTemplate, nfDefaultRefsParam,
                                       nfExecuteOnReload, nfLastRead,
                                       nfFirstWrite, nfSkipFieldChecking,
-                                      nfOpenSym}
+                                      nfDisabledOpenSym}
   namePos* = 0
   patternPos* = 1    # empty except for term rewriting macros
   genericParamsPos* = 2
@@ -897,7 +895,7 @@ const
   nfAllFieldsSet* = nfBase2
 
   nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice,
-                   nkClosedSymChoice}
+                   nkClosedSymChoice, nkOpenSym}
 
   nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit}
   nkLiterals* = {nkCharLit..nkTripleStrLit}
@@ -925,6 +923,7 @@ proc getPIdent*(a: PNode): PIdent {.inline.} =
   of nkSym: a.sym.name
   of nkIdent: a.ident
   of nkOpenSymChoice, nkClosedSymChoice: a.sons[0].sym.name
+  of nkOpenSym: getPIdent(a.sons[0])
   else: nil
 
 const
@@ -1058,8 +1057,11 @@ proc getDeclPragma*(n: PNode): PNode =
 
 proc extractPragma*(s: PSym): PNode =
   ## gets the pragma node of routine/type/var/let/const symbol `s`
-  if s.kind in routineKinds:
-    result = s.ast[pragmasPos]
+  if s.kind in routineKinds: # bug #24167
+    if s.ast[pragmasPos] != nil and s.ast[pragmasPos].kind != nkEmpty:
+      result = s.ast[pragmasPos]
+    else:
+      result = nil
   elif s.kind in {skType, skVar, skLet, skConst}:
     if s.ast != nil and s.ast.len > 0:
       if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1:
@@ -1285,6 +1287,9 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
   result.typ = sym.typ
   result.info = info
 
+proc newOpenSym*(n: PNode): PNode {.inline.} =
+  result = newTreeI(nkOpenSym, n.info, n)
+
 proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
   result = newNode(kind)
   result.intVal = intVal
@@ -1509,13 +1514,14 @@ proc newType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym; son: sink PType
 
 proc setSons*(dest: PType; sons: sink seq[PType]) {.inline.} = dest.sons = sons
 proc setSon*(dest: PType; son: sink PType) {.inline.} = dest.sons = @[son]
+proc setSonsLen*(dest: PType; len: int) {.inline.} = setLen(dest.sons, len)
 
 proc mergeLoc(a: var TLoc, b: TLoc) =
   if a.k == low(typeof(a.k)): a.k = b.k
   if a.storage == low(typeof(a.storage)): a.storage = b.storage
   a.flags.incl b.flags
   if a.lode == nil: a.lode = b.lode
-  if a.r == "": a.r = b.r
+  if a.snippet == "": a.snippet = b.snippet
 
 proc newSons*(father: PNode, length: int) =
   setLen(father.sons, length)
diff --git a/compiler/astyaml.nim b/compiler/astyaml.nim
index c7e090cd3..b0fa2bfb2 100644
--- a/compiler/astyaml.nim
+++ b/compiler/astyaml.nim
@@ -75,7 +75,7 @@ proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet;
     res.addf("\n$1storage: $2", [istr, makeYamlString($n.loc.storage)])
     if card(n.loc.flags) > 0:
       res.addf("\n$1flags: $2", [istr, makeYamlString($n.loc.flags)])
-    res.addf("\n$1r: $2", [istr, n.loc.r])
+    res.addf("\n$1snippet: $2", [istr, n.loc.snippet])
     res.addf("\n$1lode: $2", [istr])
     res.treeToYamlAux(conf, n.loc.lode, marker, indent + 1, maxRecDepth - 1)
 
diff --git a/compiler/backendpragmas.nim b/compiler/backendpragmas.nim
deleted file mode 100644
index b18644810..000000000
--- a/compiler/backendpragmas.nim
+++ /dev/null
@@ -1,25 +0,0 @@
-import pragmas, options, ast, trees
-
-proc pushBackendOption(optionsStack: var seq[TOptions], options: var TOptions) =
-  optionsStack.add options
-
-proc popBackendOption(optionsStack: var seq[TOptions], options: var TOptions) =
-  options = optionsStack[^1]
-  optionsStack.setLen(optionsStack.len-1)
-
-proc processPushBackendOption*(optionsStack: var seq[TOptions], options: var TOptions,
-                           n: PNode, start: int) =
-  pushBackendOption(optionsStack, options)
-  for i in start..<n.len:
-    let it = n[i]
-    if it.kind in nkPragmaCallKinds and it.len == 2 and it[1].kind == nkIntLit:
-      let sw = whichPragma(it[0])
-      let opts = pragmaToOptions(sw)
-      if opts != {}:
-        if it[1].intVal != 0:
-          options.incl opts
-        else:
-          options.excl opts
-
-template processPopBackendOption*(optionsStack: var seq[TOptions], options: var TOptions) =
-  popBackendOption(optionsStack, options)
diff --git a/compiler/cbuilder.nim b/compiler/cbuilder.nim
new file mode 100644
index 000000000..bb1bdfe27
--- /dev/null
+++ b/compiler/cbuilder.nim
@@ -0,0 +1,174 @@
+type
+  Snippet = string
+  Builder = string
+
+template newBuilder(s: string): Builder =
+  s
+
+proc addField(obj: var Builder; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
+  obj.add('\t')
+  obj.add(typ)
+  obj.add(" ")
+  obj.add(name)
+  if isFlexArray:
+    obj.add("[SEQ_DECL_SIZE]")
+  if initializer.len != 0:
+    obj.add(initializer)
+  obj.add(";\n")
+
+proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
+  ## for fields based on an `skField` symbol
+  obj.add('\t')
+  if field.alignment > 0:
+    obj.add("NIM_ALIGN(")
+    obj.addInt(field.alignment)
+    obj.add(") ")
+  obj.add(typ)
+  if sfNoalias in field.flags:
+    obj.add(" NIM_NOALIAS")
+  obj.add(" ")
+  obj.add(name)
+  if isFlexArray:
+    obj.add("[SEQ_DECL_SIZE]")
+  if field.bitsize != 0:
+    obj.add(":")
+    obj.addInt(field.bitsize)
+  if initializer.len != 0:
+    obj.add(initializer)
+  obj.add(";\n")
+
+type
+  BaseClassKind = enum
+    bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
+  StructBuilderInfo = object
+    baseKind: BaseClassKind
+    preFieldsLen: int
+
+proc structOrUnion(t: PType): Snippet =
+  let t = t.skipTypes({tyAlias, tySink})
+  if tfUnion in t.flags: "union"
+  else: "struct"
+
+proc ptrType(t: Snippet): Snippet =
+  t & "*"
+
+proc startSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet): StructBuilderInfo =
+  result = StructBuilderInfo(baseKind: bcNone)
+  obj.add("struct ")
+  obj.add(name)
+  if baseType.len != 0:
+    if m.compileToCpp:
+      result.baseKind = bcCppInherit
+    else:
+      result.baseKind = bcSupField
+  if result.baseKind == bcCppInherit:
+    obj.add(" : public ")
+    obj.add(baseType)
+  obj.add(" ")
+  obj.add("{\n")
+  result.preFieldsLen = obj.len
+  if result.baseKind == bcSupField:
+    obj.addField(name = "Sup", typ = baseType)
+
+proc finishSimpleStruct(obj: var Builder; m: BModule; info: StructBuilderInfo) =
+  if info.baseKind == bcNone and info.preFieldsLen == obj.len:
+    # no fields were added, add dummy field
+    obj.addField(name = "dummy", typ = "char")
+  obj.add("};\n")
+
+template addSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet; body: typed) =
+  ## for independent structs, not directly based on a Nim type
+  let info = startSimpleStruct(obj, m, name, baseType)
+  body
+  finishSimpleStruct(obj, m, info)
+
+proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType: Snippet): StructBuilderInfo =
+  result = StructBuilderInfo(baseKind: bcNone)
+  if tfPacked in t.flags:
+    if hasAttribute in CC[m.config.cCompiler].props:
+      obj.add(structOrUnion(t))
+      obj.add(" __attribute__((__packed__))")
+    else:
+      obj.add("#pragma pack(push, 1)\n")
+      obj.add(structOrUnion(t))
+  else:
+    obj.add(structOrUnion(t))
+  obj.add(" ")
+  obj.add(name)
+  if t.kind == tyObject:
+    if t.baseClass == nil:
+      if lacksMTypeField(t):
+        result.baseKind = bcNone
+      elif optTinyRtti in m.config.globalOptions:
+        result.baseKind = bcNoneTinyRtti
+      else:
+        result.baseKind = bcNoneRtti
+    elif m.compileToCpp:
+      result.baseKind = bcCppInherit
+    else:
+      result.baseKind = bcSupField
+  elif baseType.len != 0:
+    if m.compileToCpp:
+      result.baseKind = bcCppInherit
+    else:
+      result.baseKind = bcSupField
+  if result.baseKind == bcCppInherit:
+    obj.add(" : public ")
+    obj.add(baseType)
+  obj.add(" ")
+  obj.add("{\n")
+  result.preFieldsLen = obj.len
+  case result.baseKind
+  of bcNone:
+    # rest of the options add a field or don't need it due to inheritance,
+    # we need to add the dummy field for uncheckedarray ahead of time
+    # so that it remains trailing
+    if t.itemId notin m.g.graph.memberProcsPerType and
+        t.n != nil and t.n.len == 1 and t.n[0].kind == nkSym and
+        t.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
+      # only consists of flexible array field, add *initial* dummy field
+      obj.addField(name = "dummy", typ = "char")
+  of bcCppInherit: discard
+  of bcNoneRtti:
+    obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
+  of bcNoneTinyRtti:
+    obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
+  of bcSupField:
+    obj.addField(name = "Sup", typ = baseType)
+
+proc finishStruct(obj: var Builder; m: BModule; t: PType; info: StructBuilderInfo) =
+  if info.baseKind == bcNone and info.preFieldsLen == obj.len and
+      t.itemId notin m.g.graph.memberProcsPerType:
+    # no fields were added, add dummy field
+    obj.addField(name = "dummy", typ = "char")
+  obj.add("};\n")
+  if tfPacked in t.flags and hasAttribute notin CC[m.config.cCompiler].props:
+    obj.add("#pragma pack(pop)\n")
+
+template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: Snippet; body: typed) =
+  ## for structs built directly from a Nim type
+  let info = startStruct(obj, m, typ, name, baseType)
+  body
+  finishStruct(obj, m, typ, info)
+
+template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
+  ## adds a field with a `struct { ... }` type, building it according to `body`
+  obj.add('\t')
+  if tfPacked in parentTyp.flags:
+    if hasAttribute in CC[m.config.cCompiler].props:
+      obj.add("struct __attribute__((__packed__)) {\n")
+    else:
+      obj.add("#pragma pack(push, 1)\nstruct {")
+  else:
+    obj.add("struct {\n")
+  body
+  obj.add("} ")
+  obj.add(fieldName)
+  obj.add(";\n")
+  if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props:
+    result.add("#pragma pack(pop)\n")
+
+template addAnonUnion(obj: var Builder; body: typed) =
+  obj.add "union{\n"
+  body
+  obj.add("};\n")
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index 607f6d51e..ac607e3ad 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -76,6 +76,23 @@ proc isHarmlessStore(p: BProc; canRaise: bool; d: TLoc): bool =
   else:
     result = false
 
+proc cleanupTemp(p: BProc; returnType: PType, tmp: TLoc): bool =
+  if returnType.kind in {tyVar, tyLent}:
+    # we don't need to worry about var/lent return types
+    result = false
+  elif hasDestructor(returnType) and getAttachedOp(p.module.g.graph, returnType, attachedDestructor) != nil:
+    let dtor = getAttachedOp(p.module.g.graph, returnType, attachedDestructor)
+    var op = initLocExpr(p, newSymNode(dtor))
+    var callee = rdLoc(op)
+    let destroy = if dtor.typ.firstParamType.kind == tyVar:
+        callee & "(&" & rdLoc(tmp) & ")"
+      else:
+        callee & "(" & rdLoc(tmp) & ")"
+    raiseExitCleanup(p, destroy)
+    result = true
+  else:
+    result = false
+
 proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
                callee, params: Rope) =
   let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
@@ -115,7 +132,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
           # with them to prevent undefined behaviour and because the codegen
           # is free to emit expressions multiple times!
           d.k = locCall
-          d.r = pl
+          d.snippet = pl
           excl d.flags, lfSingleUse
         else:
           if d.k == locNone and p.splitDecls == 0:
@@ -123,35 +140,48 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
           else:
             if d.k == locNone: d = getTemp(p, typ.returnType)
             var list = initLoc(locCall, d.lode, OnUnknown)
-            list.r = pl
-            genAssignment(p, d, list, {}) # no need for deep copying
+            list.snippet = pl
+            genAssignment(p, d, list, {needAssignCall}) # no need for deep copying
             if canRaise: raiseExit(p)
 
       elif isHarmlessStore(p, canRaise, d):
-        if d.k == locNone: d = getTemp(p, typ.returnType)
+        var useTemp = false
+        if d.k == locNone:
+          useTemp = true
+          d = getTemp(p, typ.returnType)
         assert(d.t != nil)        # generate an assignment to d:
         var list = initLoc(locCall, d.lode, OnUnknown)
-        list.r = pl
-        genAssignment(p, d, list, flags) # no need for deep copying
-        if canRaise: raiseExit(p)
+        list.snippet = pl
+        genAssignment(p, d, list, flags+{needAssignCall}) # no need for deep copying
+        if canRaise:
+          if not (useTemp and cleanupTemp(p, typ.returnType, d)):
+            raiseExit(p)
       else:
         var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
         var list = initLoc(locCall, d.lode, OnUnknown)
-        list.r = pl
-        genAssignment(p, tmp, list, flags) # no need for deep copying
-        if canRaise: raiseExit(p)
+        list.snippet = pl
+        genAssignment(p, tmp, list, flags+{needAssignCall}) # no need for deep copying
+        if canRaise:
+          if not cleanupTemp(p, typ.returnType, tmp):
+            raiseExit(p)
         genAssignment(p, d, tmp, {})
   else:
     pl.add(");\n")
     line(p, cpsStmts, pl)
     if canRaise: raiseExit(p)
 
-proc genBoundsCheck(p: BProc; arr, a, b: TLoc)
+proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType)
 
 proc reifiedOpenArray(n: PNode): bool {.inline.} =
   var x = n
-  while x.kind in {nkAddr, nkHiddenAddr, nkHiddenStdConv, nkHiddenDeref}:
-    x = x[0]
+  while true:
+    case x.kind
+    of {nkAddr, nkHiddenAddr, nkHiddenDeref}:
+      x = x[0]
+    of nkHiddenStdConv:
+      x = x[1]
+    else:
+      break
   if x.kind == nkSym and x.sym.kind == skParam:
     result = false
   else:
@@ -161,12 +191,15 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
   var a = initLocExpr(p, q[1])
   var b = initLocExpr(p, q[2])
   var c = initLocExpr(p, q[3])
+  # bug #23321: In the function mapType, ptrs (tyPtr, tyVar, tyLent, tyRef)
+  # are mapped into ctPtrToArray, the dereference of which is skipped
+  # in the `genDeref`. We need to skip these ptrs here
+  let ty = skipTypes(a.t, abstractVar+{tyPtr, tyRef})
   # but first produce the required index checks:
   if optBoundsCheck in p.options:
-    genBoundsCheck(p, a, b, c)
+    genBoundsCheck(p, a, b, c, ty)
   if prepareForMutation:
     linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
-  let ty = skipTypes(a.t, abstractVar+{tyPtr})
   let dest = getTypeDesc(p.module, destType)
   let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)]
   case ty.kind
@@ -241,7 +274,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
           optSeqDestructors in p.config.globalOptions:
         linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
       if ntyp.kind in {tyVar} and not compileToCpp(p.module):
-        var t = TLoc(r: "(*$1)" % [a.rdLoc])
+        var t = TLoc(snippet: "(*$1)" % [a.rdLoc])
         result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
                      [a.rdLoc, lenExpr(p, t), dataField(p),
                       dataFieldAccessor(p, "*" & a.rdLoc)]
@@ -253,7 +286,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
     of tyPtr, tyRef:
       case elementType(a.t).kind
       of tyString, tySequence:
-        var t = TLoc(r: "(*$1)" % [a.rdLoc])
+        var t = TLoc(snippet: "(*$1)" % [a.rdLoc])
         result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
                      [a.rdLoc, lenExpr(p, t), dataField(p),
                       dataFieldAccessor(p, "*" & a.rdLoc)]
@@ -298,18 +331,33 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
       addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result)
   elif p.module.compileToCpp and param.typ.kind in {tyVar} and
       n.kind == nkHiddenAddr:
-    a = initLocExprSingleUse(p, n[0])
+    # bug #23748: we need to introduce a temporary here. The expression type
+    # will be a reference in C++ and we cannot create a temporary reference
+    # variable. Thus, we create a temporary pointer variable instead.
+    let needsIndirect = mapType(p.config, n[0].typ, mapTypeChooser(n[0]) == skParam) != ctArray
+    if needsIndirect:
+      n.typ = n.typ.exactReplica
+      n.typ.flags.incl tfVarIsPtr
+    a = initLocExprSingleUse(p, n)
+    a = withTmpIfNeeded(p, a, needsTmp)
+    if needsIndirect: a.flags.incl lfIndirect
     # if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still
     # means '*T'. See posix.nim for lots of examples that do that in the wild.
     let callee = call[0]
     if callee.kind == nkSym and
         {sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and
-        {lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
+        {lfHeader, lfNoDecl} * callee.sym.loc.flags != {} and
+        needsIndirect:
       addAddrLoc(p.config, a, result)
     else:
       addRdLoc(a, result)
   else:
     a = initLocExprSingleUse(p, n)
+    if param.typ.kind in {tyVar, tyPtr, tyRef, tySink}:
+      let typ = skipTypes(param.typ, abstractPtrs)
+      if not sameBackendTypePickyAliases(typ, n.typ.skipTypes(abstractPtrs)):
+        a.snippet = "(($1) ($2))" %
+          [getTypeDesc(p.module, param.typ), rdCharLoc(a)]
     addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
   #assert result != nil
 
@@ -354,7 +402,7 @@ proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) =
   of nkCallKinds:
     case n.getMagic:
     of mIncl, mExcl, mInc, mDec, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
-        mAddr, mNew, mNewFinalize, mWasMoved, mDestroy, mReset:
+        mAddr, mNew, mNewFinalize, mWasMoved, mDestroy:
       getPotentialWrites(n[1], true, result)
       for i in 2..<n.len:
         getPotentialWrites(n[i], mutate, result)
@@ -392,9 +440,11 @@ proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
         if not needTmp[i - 1]:
           needTmp[i - 1] = potentialAlias(n, potentialWrites)
       getPotentialWrites(ri[i], false, potentialWrites)
-    if ri[i].kind in {nkHiddenAddr, nkAddr}:
-      # Optimization: don't use a temp, if we would only take the address anyway
-      needTmp[i - 1] = false
+    when false:
+      # this optimization is wrong, see bug #23748
+      if ri[i].kind in {nkHiddenAddr, nkAddr}:
+        # Optimization: don't use a temp, if we would only take the address anyway
+        needTmp[i - 1] = false
 
   var oldLen = result.len
   for i in 1..<ri.len:
@@ -482,9 +532,9 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
       assert(d.t != nil)        # generate an assignment to d:
       var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
       if tfIterator in typ.flags:
-        list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
+        list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
       else:
-        list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
+        list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
       genAssignment(p, d, list, {}) # no need for deep copying
       if canRaise: raiseExit(p)
     else:
@@ -492,9 +542,9 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
       assert(d.t != nil)        # generate an assignment to d:
       var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
       if tfIterator in typ.flags:
-        list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
+        list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
       else:
-        list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
+        list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
       genAssignment(p, tmp, list, {})
       if canRaise: raiseExit(p)
       genAssignment(p, d, tmp, {})
@@ -677,7 +727,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
   var typ = skipTypes(ri[0].typ, abstractInst)
   assert(typ.kind == tyProc)
   # don't call '$' here for efficiency:
-  let pat = $ri[0].sym.loc.r
+  let pat = $ri[0].sym.loc.snippet
   internalAssert p.config, pat.len > 0
   if pat.contains({'#', '(', '@', '\''}):
     var pl = newRopeAppender()
@@ -690,13 +740,13 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
         # with them to prevent undefined behaviour and because the codegen
         # is free to emit expressions multiple times!
         d.k = locCall
-        d.r = pl
+        d.snippet = pl
         excl d.flags, lfSingleUse
       else:
         if d.k == locNone: d = getTemp(p, typ.returnType)
         assert(d.t != nil)        # generate an assignment to d:
         var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
-        list.r = pl
+        list.snippet = pl
         genAssignment(p, d, list, {}) # no need for deep copying
     else:
       pl.add(";\n")
@@ -706,7 +756,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
     var argsCounter = 0
     if 1 < ri.len:
       genThisArg(p, ri, 1, typ, pl)
-    pl.add(op.r)
+    pl.add(op.snippet)
     var params = newRopeAppender()
     for i in 2..<ri.len:
       genOtherArg(p, ri, i, typ, params, argsCounter)
@@ -721,12 +771,12 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
   assert(typ.kind == tyProc)
 
   # don't call '$' here for efficiency:
-  let pat = $ri[0].sym.loc.r
+  let pat = $ri[0].sym.loc.snippet
   internalAssert p.config, pat.len > 0
   var start = 3
   if ' ' in pat:
     start = 1
-    pl.add(op.r)
+    pl.add(op.snippet)
     if ri.len > 1:
       pl.add(": ")
       genArg(p, ri[1], typ.n[1].sym, ri, pl)
@@ -735,7 +785,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
     if ri.len > 1:
       genArg(p, ri[1], typ.n[1].sym, ri, pl)
       pl.add(" ")
-    pl.add(op.r)
+    pl.add(op.snippet)
     if ri.len > 2:
       pl.add(": ")
       genArg(p, ri[2], typ.n[2].sym, ri, pl)
@@ -770,7 +820,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
       if d.k == locNone: d = getTemp(p, typ.returnType)
       assert(d.t != nil)        # generate an assignment to d:
       var list: TLoc = initLoc(locCall, ri, OnUnknown)
-      list.r = pl
+      list.snippet = pl
       genAssignment(p, d, list, {}) # no need for deep copying
   else:
     pl.add("];\n")
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 042af01f1..545d43ae8 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -224,7 +224,7 @@ proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc =
   result = TLoc(k: locField,
     storage: a.storage,
     lode: lodeTyp t,
-    r: rdLoc(a) & "." & field
+    snippet: rdLoc(a) & "." & field
   )
 
 proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
@@ -254,9 +254,9 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags,
   case t.kind
   of nkSym:
     let field = t.sym
-    if field.loc.r == "": fillObjectFields(p.module, typ)
-    genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.r),
-                     optAsgnLoc(src, field.typ, field.loc.r), newflags)
+    if field.loc.snippet == "": fillObjectFields(p.module, typ)
+    genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.snippet),
+                     optAsgnLoc(src, field.typ, field.loc.snippet), newflags)
   of nkRecList:
     for child in items(t): genOptAsgnObject(p, dest, src, newflags, child, typ)
   else: discard
@@ -343,7 +343,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   of tyString:
     if optSeqDestructors in p.config.globalOptions:
       genGenericAsgn(p, dest, src, flags)
-    elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode, dest):
+    elif ({needToCopy, needToCopySinkParam} * flags == {} and src.storage != OnStatic) or canMove(p, src.lode, dest):
       genRefAssign(p, dest, src)
     else:
       if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config):
@@ -379,7 +379,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     elif not isObjLackingTypeField(ty):
       genGenericAsgn(p, dest, src, flags)
     elif containsGarbageCollectedRef(ty):
-      if ty[0].isNil and asgnComplexity(ty.n) <= 4:
+      if ty[0].isNil and asgnComplexity(ty.n) <= 4 and
+            needAssignCall notin flags: # calls might contain side effects
         discard getTypeDesc(p.module, ty)
         internalAssert p.config, ty.n != nil
         genOptAsgnObject(p, dest, src, flags, ty.n, ty)
@@ -482,7 +483,7 @@ proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) =
   if d.k != locNone:
     var a: TLoc = initLoc(locData, n, OnStatic)
     # need to generate an assignment here
-    a.r = r
+    a.snippet = r
     if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
     else: genAssignment(p, d, a, {needToCopy})
   else:
@@ -490,13 +491,13 @@ proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) =
     # the flags field!
     d.k = locData
     d.lode = n
-    d.r = r
+    d.snippet = r
 
 proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) =
   if d.k != locNone:
     # need to generate an assignment here
     var a: TLoc = initLoc(locExpr, n, s)
-    a.r = r
+    a.snippet = r
     if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
     else: genAssignment(p, d, a, {needToCopy})
   else:
@@ -504,7 +505,7 @@ proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) =
     # the flags field!
     d.k = locExpr
     d.lode = n
-    d.r = r
+    d.snippet = r
 
 proc binaryStmt(p: BProc, e: PNode, d: var TLoc, op: string) =
   if d.k != locNone: internalError(p.config, e.info, "binaryStmt")
@@ -589,7 +590,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   # skipping 'range' is correct here as we'll generate a proper range check
   # later via 'chckRange'
   let t = e.typ.skipTypes(abstractRange)
-  if optOverflowCheck notin p.options:
+  if optOverflowCheck notin p.options or (m in {mSucc, mPred} and t.kind in {tyUInt..tyUInt64}):
     let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
     putIntoDest(p, d, e, res)
   else:
@@ -808,10 +809,12 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) =
   # careful  'addr(myptrToArray)' needs to get the ampersand:
   if e[0].typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr}:
     var a: TLoc = initLocExpr(p, e[0])
-    putIntoDest(p, d, e, "&" & a.r, a.storage)
+    putIntoDest(p, d, e, "&" & a.snippet, a.storage)
     #Message(e.info, warnUser, "HERE NEW &")
   elif mapType(p.config, e[0].typ, mapTypeChooser(e[0]) == skParam) == ctArray or isCppRef(p, e.typ):
     expr(p, e[0], d)
+    # bug #19497
+    d.lode = e
   else:
     var a: TLoc = initLocExpr(p, e[0])
     putIntoDest(p, d, e, addrLoc(p.config, a), a.storage)
@@ -877,10 +880,10 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
   else:
     var rtyp: PType = nil
     let field = lookupFieldAgain(p, ty, f, r, addr rtyp)
-    if field.loc.r == "" and rtyp != nil: fillObjectFields(p.module, rtyp)
-    if field.loc.r == "": internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty))
+    if field.loc.snippet == "" and rtyp != nil: fillObjectFields(p.module, rtyp)
+    if field.loc.snippet == "": internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty))
     r.add "."
-    r.add field.loc.r
+    r.add field.loc.snippet
     putIntoDest(p, d, e, r, a.storage)
   r.freeze
 
@@ -899,10 +902,10 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
     test = initLoc(locNone, it, OnStack)
     u = initLocExpr(p, it[1])
     v = initLoc(locExpr, disc, OnUnknown)
-    v.r = newRopeAppender()
-    v.r.add obj
-    v.r.add(".")
-    v.r.add(disc.sym.loc.r)
+    v.snippet = newRopeAppender()
+    v.snippet.add obj
+    v.snippet.add(".")
+    v.snippet.add(disc.sym.loc.snippet)
     genInExprAux(p, it, u, v, test)
     var msg = ""
     if optDeclaredLocs in p.config.globalOptions:
@@ -960,12 +963,12 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
     var r = rdLoc(a)
     let f = e[0][1].sym
     let field = lookupFieldAgain(p, ty, f, r)
-    if field.loc.r == "": fillObjectFields(p.module, ty)
-    if field.loc.r == "":
+    if field.loc.snippet == "": fillObjectFields(p.module, ty)
+    if field.loc.snippet == "":
       internalError(p.config, e.info, "genCheckedRecordField") # generate the checks:
     genFieldCheck(p, e, r, field)
     r.add(".")
-    r.add field.loc.r
+    r.add field.loc.snippet
     putIntoDest(p, d, e[0], r, a.storage)
     r.freeze
   else:
@@ -1019,8 +1022,8 @@ proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) =
   putIntoDest(p, d, n,
               ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage)
 
-proc genBoundsCheck(p: BProc; arr, a, b: TLoc) =
-  let ty = skipTypes(arr.t, abstractVarRange)
+proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType) =
+  let ty = arrTyp
   case ty.kind
   of tyOpenArray, tyVarargs:
     if reifiedOpenArray(arr.lode):
@@ -1100,7 +1103,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
 
   if d.k == locNone: d.storage = OnHeap
   if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
-    a.r = ropecg(p.module, "(*$1)", [a.r])
+    a.snippet = ropecg(p.module, "(*$1)", [a.snippet])
 
   if lfPrepareForMutation in d.flags and ty.kind == tyString and
       optSeqDestructors in p.config.globalOptions:
@@ -1166,9 +1169,9 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
     var tmpB = initLocExprSingleUse(p, e[2])
     tmpB.k = locExpr
     if m == mOr:
-      tmpB.r = "((" & rdLoc(tmpA) & ")||(" & rdLoc(tmpB) & "))"
+      tmpB.snippet = "((" & rdLoc(tmpA) & ")||(" & rdLoc(tmpB) & "))"
     else:
-      tmpB.r = "((" & rdLoc(tmpA) & ")&&(" & rdLoc(tmpB) & "))"
+      tmpB.snippet = "((" & rdLoc(tmpA) & ")&&(" & rdLoc(tmpB) & "))"
     if d.k == locNone:
       d = tmpB
     else:
@@ -1271,7 +1274,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
         lens.add(lenExpr(p, a))
         lens.add(" + ")
       appends.add(ropecg(p.module, "#appendString($1, $2);$n", [strLoc(p, tmp), rdLoc(a)]))
-  linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", [tmp.r, lens, L])
+  linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", [tmp.snippet, lens, L])
   p.s(cpsStmts).add appends
   if d.k == locNone:
     d = tmp
@@ -1317,7 +1320,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
             [byRefLoc(p, dest), lens, L])
   else:
     call = initLoc(locCall, e, OnHeap)
-    call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, L])
+    call.snippet = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, L])
     genAssignment(p, dest, call, {})
     gcUsage(p.config, e)
   p.s(cpsStmts).add appends
@@ -1332,12 +1335,12 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
   var call = initLoc(locCall, e, OnHeap)
   if not p.module.compileToCpp:
     const seqAppendPattern = "($2) #incrSeqV3((TGenericSeq*)($1), $3)"
-    call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a),
+    call.snippet = ropecg(p.module, seqAppendPattern, [rdLoc(a),
       getTypeDesc(p.module, e[1].typ),
       genTypeInfoV1(p.module, seqType, e.info)])
   else:
     const seqAppendPattern = "($2) #incrSeqV3($1, $3)"
-    call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a),
+    call.snippet = ropecg(p.module, seqAppendPattern, [rdLoc(a),
       getTypeDesc(p.module, e[1].typ),
       genTypeInfoV1(p.module, seqType, e.info)])
   # emit the write barrier if required, but we can always move here, so
@@ -1347,19 +1350,11 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
   #  echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
   var dest = initLoc(locExpr, e[2], OnHeap)
   var tmpL = getIntTemp(p)
-  lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.r, rdLoc(a), lenField(p)])
-  dest.r = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.r, dataField(p)])
+  lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.snippet, rdLoc(a), lenField(p)])
+  dest.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.snippet, dataField(p)])
   genAssignment(p, dest, b, {needToCopy})
   gcUsage(p.config, e)
 
-proc genReset(p: BProc, n: PNode) =
-  var a: TLoc = initLocExpr(p, n[1])
-  specializeReset(p, a)
-  when false:
-    linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
-            [addrLoc(p.config, a),
-            genTypeInfoV1(p.module, skipTypes(a.t, {tyVar}), n.info)])
-
 proc genDefault(p: BProc; n: PNode; d: var TLoc) =
   if d.k == locNone: d = getTemp(p, n.typ, needsInit=true)
   else: resetLoc(p, d)
@@ -1376,10 +1371,10 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) =
 
   if optTinyRtti in p.config.globalOptions:
     if needsInit:
-      b.r = ropecg(p.module, "($1) #nimNewObj($2, NIM_ALIGNOF($3))",
+      b.snippet = ropecg(p.module, "($1) #nimNewObj($2, NIM_ALIGNOF($3))",
           [getTypeDesc(p.module, typ), sizeExpr, getTypeDesc(p.module, bt)])
     else:
-      b.r = ropecg(p.module, "($1) #nimNewObjUninit($2, NIM_ALIGNOF($3))",
+      b.snippet = ropecg(p.module, "($1) #nimNewObjUninit($2, NIM_ALIGNOF($3))",
           [getTypeDesc(p.module, typ), sizeExpr, getTypeDesc(p.module, bt)])
     genAssignment(p, a, b, {})
   else:
@@ -1404,15 +1399,15 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) =
       if p.config.selectedGC == gcGo:
         # newObjRC1() would clash with unsureAsgnRef() - which is used by gcGo to
         # implement the write barrier
-        b.r = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
+        b.snippet = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
         linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n",
                 [addrLoc(p.config, a), b.rdLoc])
       else:
         # use newObjRC1 as an optimization
-        b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
+        b.snippet = ropecg(p.module, "($1) #newObjRC1($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
         linefmt(p, cpsStmts, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
     else:
-      b.r = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
+      b.snippet = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
       genAssignment(p, a, b, {})
   # set the object type:
   genObjectInit(p, cpsStmts, bt, a, constructRefObj)
@@ -1438,18 +1433,18 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) =
     if not lenIsZero:
       if p.config.selectedGC == gcGo:
         # we need the write barrier
-        call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
+        call.snippet = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
               genTypeInfoV1(p.module, seqtype, dest.lode.info), length])
         linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", [addrLoc(p.config, dest), call.rdLoc])
       else:
-        call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", [getTypeDesc(p.module, seqtype),
+        call.snippet = ropecg(p.module, "($1) #newSeqRC1($2, $3)", [getTypeDesc(p.module, seqtype),
               genTypeInfoV1(p.module, seqtype, dest.lode.info), length])
         linefmt(p, cpsStmts, "$1 = $2;$n", [dest.rdLoc, call.rdLoc])
   else:
     if lenIsZero:
-      call.r = rope"NIM_NIL"
+      call.snippet = rope"NIM_NIL"
     else:
-      call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
+      call.snippet = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
               genTypeInfoV1(p.module, seqtype, dest.lode.info), length])
     genAssignment(p, dest, call, {})
 
@@ -1492,9 +1487,13 @@ proc rawConstExpr(p: BProc, n: PNode; d: var TLoc) =
   if id == p.module.labels:
     # expression not found in the cache:
     inc(p.module.labels)
-    p.module.s[cfsData].addf("static NIM_CONST $1 $2 = ", [getTypeDesc(p.module, t), d.r])
-    genBracedInit(p, n, isConst = true, t, p.module.s[cfsData])
-    p.module.s[cfsData].addf(";$n", [])
+    var data = "static NIM_CONST $1 $2 = " % [getTypeDesc(p.module, t), d.snippet]
+    # bug #23627; when generating const object fields, it's likely that
+    # we need to generate type infos for the object, which may be an object with
+    # custom hooks. We need to generate potential consts in the hooks first.
+    genBracedInit(p, n, isConst = true, t, data)
+    data.addf(";$n", [])
+    p.module.s[cfsData].add data
 
 proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
   if d.k == locNone and n.len > ord(n.kind == nkObjConstr) and n.isDeepConstExpr:
@@ -1505,15 +1504,14 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
 
 
 proc genFieldObjConstr(p: BProc; ty: PType; useTemp, isRef: bool; nField, val, check: PNode; d: var TLoc; r: Rope; info: TLineInfo) =
-  var tmp2: TLoc = default(TLoc)
-  tmp2.r = r
-  let field = lookupFieldAgain(p, ty, nField.sym, tmp2.r)
-  if field.loc.r == "": fillObjectFields(p.module, ty)
-  if field.loc.r == "": internalError(p.config, info, "genFieldObjConstr")
+  var tmp2 = TLoc(snippet: r)
+  let field = lookupFieldAgain(p, ty, nField.sym, tmp2.snippet)
+  if field.loc.snippet == "": fillObjectFields(p.module, ty)
+  if field.loc.snippet == "": internalError(p.config, info, "genFieldObjConstr")
   if check != nil and optFieldCheck in p.options:
     genFieldCheck(p, check, r, field)
-  tmp2.r.add(".")
-  tmp2.r.add(field.loc.r)
+  tmp2.snippet.add(".")
+  tmp2.snippet.add(field.loc.snippet)
   if useTemp:
     tmp2.k = locTemp
     tmp2.storage = if isRef: OnHeap else: OnStack
@@ -1521,7 +1519,12 @@ proc genFieldObjConstr(p: BProc; ty: PType; useTemp, isRef: bool; nField, val, c
     tmp2.k = d.k
     tmp2.storage = if isRef: OnHeap else: d.storage
   tmp2.lode = val
-  expr(p, val, tmp2)
+  if nField.typ.skipTypes(abstractVar).kind in {tyOpenArray, tyVarargs}:
+    var tmp3 = getTemp(p, val.typ)
+    expr(p, val, tmp3)
+    genOpenArrayConv(p, tmp2, tmp3, {})
+  else:
+    expr(p, val, tmp2)
 
 proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
   # inheritance in C++ does not allow struct initialization so
@@ -1607,7 +1610,7 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
     arr = initLoc(locExpr, n[i], OnHeap)
     var lit = newRopeAppender()
     intLiteral(i, lit)
-    arr.r = ropecg(p.module, "$1$3[$2]", [rdLoc(dest[]), lit, dataField(p)])
+    arr.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(dest[]), lit, dataField(p)])
     arr.storage = OnHeap            # we know that sequences are on the heap
     expr(p, n[i], arr)
   gcUsage(p.config, n)
@@ -1643,19 +1646,19 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
       elem = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
       var lit = newRopeAppender()
       intLiteral(i, lit)
-      elem.r = ropecg(p.module, "$1$3[$2]", [rdLoc(d), lit, dataField(p)])
+      elem.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(d), lit, dataField(p)])
       elem.storage = OnHeap # we know that sequences are on the heap
       arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n[1].typ, abstractInst)), a.storage)
-      arr.r = ropecg(p.module, "$1[$2]", [rdLoc(a), lit])
+      arr.snippet = ropecg(p.module, "$1[$2]", [rdLoc(a), lit])
       genAssignment(p, elem, arr, {needToCopy})
   else:
     var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt))
-    linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",  [i.r, L])
+    linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",  [i.snippet, L])
     elem = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
-    elem.r = ropecg(p.module, "$1$3[$2]", [rdLoc(d), rdLoc(i), dataField(p)])
+    elem.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(d), rdLoc(i), dataField(p)])
     elem.storage = OnHeap # we know that sequences are on the heap
     arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n[1].typ, abstractInst)), a.storage)
-    arr.r = ropecg(p.module, "$1[$2]", [rdLoc(a), rdLoc(i)])
+    arr.snippet = ropecg(p.module, "$1[$2]", [rdLoc(a), rdLoc(i)])
     genAssignment(p, elem, arr, {needToCopy})
     lineF(p, cpsStmts, "}$n", [])
 
@@ -1671,7 +1674,7 @@ proc genNewFinalize(p: BProc, e: PNode) =
   b = initLoc(locExpr, a.lode, OnHeap)
   ti = genTypeInfo(p.config, p.module, refType, e.info)
   p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
-  b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [
+  b.snippet = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [
       getTypeDesc(p.module, refType),
       ti, getTypeDesc(p.module, skipTypes(refType.elementType, abstractRange))])
   genAssignment(p, a, b, {})  # set the object type:
@@ -1836,7 +1839,7 @@ proc genAccessTypeField(p: BProc; e: PNode; d: var TLoc) =
 
 template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) =
   var a: TLoc = initLocExpr(p, n[1])
-  a.r = ropecg(p.module, frmt, [rdLoc(a)])
+  a.snippet = ropecg(p.module, frmt, [rdLoc(a)])
   a.flags.excl lfIndirect # this flag should not be propagated here (not just for HCR)
   if d.k == locNone: d = getTemp(p, n.typ)
   genAssignment(p, d, a, {})
@@ -1855,7 +1858,7 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       var b = initLocExpr(p, a[2])
       var c = initLocExpr(p, a[3])
       if optBoundsCheck in p.options:
-        genBoundsCheck(p, m, b, c)
+        genBoundsCheck(p, m, b, c, skipTypes(m.t, abstractVarRange))
       if op == mHigh:
         putIntoDest(p, d, e, ropecg(p.module, "(($2)-($1))", [rdLoc(b), rdLoc(c)]))
       else:
@@ -1890,8 +1893,8 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     var a = initLocExpr(p, e[1])
     var x = lenExpr(p, a)
     if op == mHigh: x = "($1-1)" % [x]
-    lineCg(p, cpsStmts, "$1 = $2;$n", [tmp.r, x])
-    putIntoDest(p, d, e, tmp.r)
+    lineCg(p, cpsStmts, "$1 = $2;$n", [tmp.snippet, x])
+    putIntoDest(p, d, e, tmp.snippet)
   of tyArray:
     # YYY: length(sideeffect) is optimized away incorrectly?
     if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(p.config, typ)))
@@ -1913,13 +1916,13 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
   var call = initLoc(locCall, e, OnHeap)
   if not p.module.compileToCpp:
     const setLenPattern = "($3) #setLengthSeqV2(($1)?&($1)->Sup:NIM_NIL, $4, $2)"
-    call.r = ropecg(p.module, setLenPattern, [
+    call.snippet = ropecg(p.module, setLenPattern, [
       rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
       genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)])
 
   else:
     const setLenPattern = "($3) #setLengthSeqV2($1, $4, $2)"
-    call.r = ropecg(p.module, setLenPattern, [
+    call.snippet = ropecg(p.module, setLenPattern, [
       rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
       genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)])
 
@@ -1935,7 +1938,7 @@ proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
     var b = initLocExpr(p, e[2])
 
     var call = initLoc(locCall, e, OnHeap)
-    call.r = ropecg(p.module, "#setLengthStr($1, $2)", [
+    call.snippet = ropecg(p.module, "#setLengthStr($1, $2)", [
         rdLoc(a), rdLoc(b)])
     genAssignment(p, a, call, {})
     gcUsage(p.config, e)
@@ -2012,23 +2015,23 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) =
     a = initLocExpr(p, ea)
     b = initLoc(locExpr, e, OnUnknown)
     if e[1].len > 0:
-      b.r = rope("(")
+      b.snippet = rope("(")
       for i in 0..<e[1].len:
         let it = e[1][i]
         if it.kind == nkRange:
           x = initLocExpr(p, it[0])
           y = initLocExpr(p, it[1])
-          b.r.addf("$1 >= $2 && $1 <= $3",
+          b.snippet.addf("$1 >= $2 && $1 <= $3",
                [rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)])
         else:
           x = initLocExpr(p, it)
-          b.r.addf("$1 == $2", [rdCharLoc(a), rdCharLoc(x)])
-        if i < e[1].len - 1: b.r.add(" || ")
-      b.r.add(")")
+          b.snippet.addf("$1 == $2", [rdCharLoc(a), rdCharLoc(x)])
+        if i < e[1].len - 1: b.snippet.add(" || ")
+      b.snippet.add(")")
     else:
       # handle the case of an empty set
-      b.r = rope("0")
-    putIntoDest(p, d, e, b.r)
+      b.snippet = rope("0")
+    putIntoDest(p, d, e, b.snippet)
   else:
     assert(e[1].typ != nil)
     assert(e[2].typ != nil)
@@ -2166,7 +2169,7 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) =
     inc(p.labels)
     var lbl = p.labels.rope
     var tmp: TLoc = default(TLoc)
-    tmp.r = "LOC$1.source" % [lbl]
+    tmp.snippet = "LOC$1.source" % [lbl]
     let destsize = getSize(p.config, destt)
     let srcsize = getSize(p.config, srct)
 
@@ -2231,12 +2234,16 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc) =
       raiseInstr(p, p.s(cpsStmts))
       linefmt p, cpsStmts, "}$n", []
 
-  putIntoDest(p, d, n, "(($1) ($2))" %
+  if sameBackendTypeIgnoreRange(dest, n[0].typ):
+    # don't cast so an address can be taken for `var` conversions
+    putIntoDest(p, d, n, "($1)" % [rdCharLoc(a)], a.storage)
+  else:
+    putIntoDest(p, d, n, "(($1) ($2))" %
       [getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)
 
 proc genConv(p: BProc, e: PNode, d: var TLoc) =
   let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink})
-  if sameBackendType(destType, e[1].typ):
+  if sameBackendTypeIgnoreRange(destType, e[1].typ):
     expr(p, e[1], d)
   else:
     genSomeCast(p, e, d)
@@ -2345,8 +2352,13 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) =
           else:
             linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), byRefLoc(p, a)])
     else:
-      let flags = if not canMove(p, n[1], d): {needToCopy} else: {}
-      genAssignment(p, d, a, flags)
+      if n[1].kind == nkSym and isSinkParam(n[1].sym):
+        var tmp = getTemp(p, n[1].typ.skipTypes({tySink}))
+        genAssignment(p, tmp, a, {needToCopySinkParam})
+        genAssignment(p, d, tmp, {})
+        resetLoc(p, tmp)
+      else:
+        genAssignment(p, d, a, {})
       resetLoc(p, a)
 
 proc genDestroy(p: BProc; n: PNode) =
@@ -2454,7 +2466,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       var call = initLoc(locCall, e, OnHeap)
       var dest = initLocExpr(p, e[1])
       var b = initLocExpr(p, e[2])
-      call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)])
+      call.snippet = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)])
       genAssignment(p, dest, call, {})
   of mAppendStrStr: genStrAppend(p, e, d)
   of mAppendSeqElem:
@@ -2518,7 +2530,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     let member =
       if t.kind == tyTuple:
         "Field" & rope(dotExpr[1].sym.position)
-      else: dotExpr[1].sym.loc.r
+      else: dotExpr[1].sym.loc.snippet
     putIntoDest(p,d,e, "((NI)offsetof($1, $2))" % [tname, member])
   of mChr: genSomeCast(p, e, d)
   of mOrd: genOrd(p, e, d)
@@ -2541,8 +2553,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     # - not sure, and it wouldn't work if the symbol behind the magic isn't
     #   somehow forward-declared from some other usage, but it is *possible*
     if lfNoDecl notin opr.loc.flags:
-      let prc = magicsys.getCompilerProc(p.module.g.graph, $opr.loc.r)
-      assert prc != nil, $opr.loc.r
+      let prc = magicsys.getCompilerProc(p.module.g.graph, $opr.loc.snippet)
+      assert prc != nil, $opr.loc.snippet
       # HACK:
       # Explicitly add this proc as declared here so the cgsym call doesn't
       # add a forward declaration - without this we could end up with the same
@@ -2555,14 +2567,13 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       let wasDeclared = containsOrIncl(p.module.declaredProtos, prc.id)
       # Make the function behind the magic get actually generated - this will
       # not lead to a forward declaration! The genCall will lead to one.
-      cgsym(p.module, $opr.loc.r)
+      cgsym(p.module, $opr.loc.snippet)
       # make sure we have pointer-initialising code for hot code reloading
       if not wasDeclared and p.hcrOn:
         p.module.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n",
              [mangleDynLibProc(prc), getTypeDesc(p.module, prc.loc.t), getModuleDllPath(p.module, prc)])
     genCall(p, e, d)
   of mDefault, mZeroDefault: genDefault(p, e, d)
-  of mReset: genReset(p, e)
   of mEcho: genEcho(p, e[1].skipConv)
   of mArrToSeq: genArrToSeq(p, e, d)
   of mNLen..mNError, mSlurp..mQuoteAst:
@@ -2685,7 +2696,7 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
       var it = n[i]
       if it.kind == nkExprColonExpr: it = it[1]
       rec = initLoc(locExpr, it, dest[].storage)
-      rec.r = "$1.Field$2" % [rdLoc(dest[]), rope(i)]
+      rec.snippet = "$1.Field$2" % [rdLoc(dest[]), rope(i)]
       rec.flags.incl(lfEnforceDeref)
       expr(p, it, rec)
 
@@ -2735,12 +2746,12 @@ proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) =
       arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), d.storage)
       var lit = newRopeAppender()
       intLiteral(i, lit)
-      arr.r = "$1[$2]" % [rdLoc(d), lit]
+      arr.snippet = "$1[$2]" % [rdLoc(d), lit]
       expr(p, n[i], arr)
 
 proc genComplexConst(p: BProc, sym: PSym, d: var TLoc) =
   requestConstImpl(p, sym)
-  assert((sym.loc.r != "") and (sym.loc.t != nil))
+  assert((sym.loc.snippet != "") and (sym.loc.t != nil))
   putLocIntoDest(p, d, sym.loc)
 
 template genStmtListExprImpl(exprOrStmt) {.dirty.} =
@@ -2872,24 +2883,24 @@ proc genConstSetup(p: BProc; sym: PSym): bool =
   result = lfNoDecl notin sym.loc.flags
 
 proc genConstHeader(m, q: BModule; p: BProc, sym: PSym) =
-  if sym.loc.r == "":
+  if sym.loc.snippet == "":
     if not genConstSetup(p, sym): return
-  assert(sym.loc.r != "", $sym.name.s & $sym.itemId)
+  assert(sym.loc.snippet != "", $sym.name.s & $sym.itemId)
   if m.hcrOn:
-    m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.r]);
+    m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.snippet]);
     m.initProc.procSec(cpsLocals).addf(
-      "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
+      "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.snippet,
       getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(q, sym)])
   else:
     let headerDecl = "extern NIM_CONST $1 $2;$n" %
-        [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.r]
+        [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.snippet]
     m.s[cfsData].add(headerDecl)
     if sfExportc in sym.flags and p.module.g.generatedHeader != nil:
       p.module.g.generatedHeader.s[cfsData].add(headerDecl)
 
 proc genConstDefinition(q: BModule; p: BProc; sym: PSym) =
   # add a suffix for hcr - will later init the global pointer with this data
-  let actualConstName = if q.hcrOn: sym.loc.r & "_const" else: sym.loc.r
+  let actualConstName = if q.hcrOn: sym.loc.snippet & "_const" else: sym.loc.snippet
   var data = newRopeAppender()
   data.addf("N_LIB_PRIVATE NIM_CONST $1 $2 = ",
            [getTypeDesc(q, sym.typ), actualConstName])
@@ -2898,16 +2909,16 @@ proc genConstDefinition(q: BModule; p: BProc; sym: PSym) =
   q.s[cfsData].add data
   if q.hcrOn:
     # generate the global pointer with the real name
-    q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, dkVar), sym.loc.r])
+    q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, dkVar), sym.loc.snippet])
     # register it (but ignore the boolean result of hcrRegisterGlobal)
     q.initProc.procSec(cpsLocals).addf(
       "\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n",
-      [getModuleDllPath(q, sym), sym.loc.r, rdLoc(sym.loc)])
+      [getModuleDllPath(q, sym), sym.loc.snippet, rdLoc(sym.loc)])
     # always copy over the contents of the actual constant with the _const
     # suffix ==> this means that the constant is reloadable & updatable!
     q.initProc.procSec(cpsLocals).add(ropecg(q,
       "\t#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n",
-      [sym.loc.r, actualConstName, rdLoc(sym.loc)]))
+      [sym.loc.snippet, actualConstName, rdLoc(sym.loc)]))
 
 proc genConstStmt(p: BProc, n: PNode) =
   # This code is only used in the new DCE implementation.
@@ -2947,7 +2958,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
         genProcPrototype(p.module, sym)
       else:
         genProc(p.module, sym)
-      if sym.loc.r == "" or sym.loc.lode == nil:
+      if sym.loc.snippet == "" or sym.loc.lode == nil:
         internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
       putLocIntoDest(p, d, sym.loc)
     of skConst:
@@ -2957,7 +2968,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
         putIntoDest(p, d, n, lit, OnStatic)
       elif useAliveDataFromDce in p.module.flags:
         genConstHeader(p.module, p.module, p, sym)
-        assert((sym.loc.r != "") and (sym.loc.t != nil))
+        assert((sym.loc.snippet != "") and (sym.loc.t != nil))
         putLocIntoDest(p, d, sym.loc)
       else:
         genComplexConst(p, sym, d)
@@ -2972,14 +2983,14 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
         if sfCompileTime in sym.flags:
           genSingleVar(p, sym, n, astdef(sym))
 
-      if sym.loc.r == "" or sym.loc.t == nil:
+      if sym.loc.snippet == "" or sym.loc.t == nil:
         #echo "FAILED FOR PRCO ", p.prc.name.s
         #echo renderTree(p.prc.ast, {renderIds})
         internalError p.config, n.info, "expr: var not init " & sym.name.s & "_" & $sym.id
       if sfThread in sym.flags:
         accessThreadLocalVar(p, sym)
         if emulatedThreadVars(p.config):
-          putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.r)
+          putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.snippet)
         else:
           putLocIntoDest(p, d, sym.loc)
       else:
@@ -2987,17 +2998,17 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
     of skTemp:
       when false:
         # this is more harmful than helpful.
-        if sym.loc.r == "":
+        if sym.loc.snippet == "":
           # we now support undeclared 'skTemp' variables for easier
           # transformations in other parts of the compiler:
           assignLocalVar(p, n)
-      if sym.loc.r == "" or sym.loc.t == nil:
+      if sym.loc.snippet == "" or sym.loc.t == nil:
         #echo "FAILED FOR PRCO ", p.prc.name.s
         #echo renderTree(p.prc.ast, {renderIds})
         internalError(p.config, n.info, "expr: temp not init " & sym.name.s & "_" & $sym.id)
       putLocIntoDest(p, d, sym.loc)
     of skParam:
-      if sym.loc.r == "" or sym.loc.t == nil:
+      if sym.loc.snippet == "" or sym.loc.t == nil:
         # echo "FAILED FOR PRCO ", p.prc.name.s
         # debug p.prc.typ.n
         # echo renderTree(p.prc.ast, {renderIds})
@@ -3087,7 +3098,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
   of nkLambdaKinds:
     var sym = n[namePos].sym
     genProc(p.module, sym)
-    if sym.loc.r == "" or sym.loc.lode == nil:
+    if sym.loc.snippet == "" or sym.loc.lode == nil:
       internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
     putLocIntoDest(p, d, sym.loc)
   of nkClosure: genClosure(p, n, d)
@@ -3118,7 +3129,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
     if ex.kind != nkEmpty:
       genLineDir(p, n)
       var a: TLoc = initLocExprSingleUse(p, ex)
-      line(p, cpsStmts, "(void)(" & a.r & ");\L")
+      line(p, cpsStmts, "(void)(" & a.snippet & ");\L")
   of nkAsmStmt: genAsmStmt(p, n)
   of nkTryStmt, nkHiddenTryStmt:
     case p.config.exc
@@ -3240,7 +3251,8 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
       getNullValueAux(p, t, it, constOrNil, result, count, isConst, info)
   of nkRecCase:
     getNullValueAux(p, t, obj[0], constOrNil, result, count, isConst, info)
-    if count > 0: result.add ", "
+    var res = ""
+    if count > 0: res.add ", "
     var branch = Zero
     if constOrNil != nil:
       ## find kind value, default is zero if not specified
@@ -3254,18 +3266,21 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
           break
 
     let selectedBranch = caseObjDefaultBranch(obj, branch)
-    result.add "{"
+    res.add "{"
     var countB = 0
     let b = lastSon(obj[selectedBranch])
     # designated initilization is the only way to init non first element of unions
     # branches are allowed to have no members (b.len == 0), in this case they don't need initializer
     if b.kind == nkRecList and not isEmptyCaseObjectBranch(b):
-      result.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {"
-      getNullValueAux(p, t,  b, constOrNil, result, countB, isConst, info)
-      result.add "}"
+      res.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {"
+      getNullValueAux(p, t,  b, constOrNil, res, countB, isConst, info)
+      res.add "}"
     elif b.kind == nkSym:
-      result.add "." & mangleRecFieldName(p.module, b.sym) & " = "
-      getNullValueAux(p, t,  b, constOrNil, result, countB, isConst, info)
+      res.add "." & mangleRecFieldName(p.module, b.sym) & " = "
+      getNullValueAux(p, t,  b, constOrNil, res, countB, isConst, info)
+    else:
+      return
+    result.add res
     result.add "}"
 
   of nkSym:
diff --git a/compiler/ccgreset.nim b/compiler/ccgreset.nim
index 4af690e35..6caeb8084 100644
--- a/compiler/ccgreset.nim
+++ b/compiler/ccgreset.nim
@@ -24,10 +24,10 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
   of nkRecCase:
     if (n[0].kind != nkSym): internalError(p.config, n.info, "specializeResetN")
     let disc = n[0].sym
-    if disc.loc.r == "": fillObjectFields(p.module, typ)
+    if disc.loc.snippet == "": fillObjectFields(p.module, typ)
     if disc.loc.t == nil:
       internalError(p.config, n.info, "specializeResetN()")
-    lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
+    lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.snippet])
     for i in 1..<n.len:
       let branch = n[i]
       assert branch.kind in {nkOfBranch, nkElse}
@@ -38,14 +38,14 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
       specializeResetN(p, accessor, lastSon(branch), typ)
       lineF(p, cpsStmts, "break;$n", [])
     lineF(p, cpsStmts, "} $n", [])
-    specializeResetT(p, "$1.$2" % [accessor, disc.loc.r], disc.loc.t)
+    specializeResetT(p, "$1.$2" % [accessor, disc.loc.snippet], disc.loc.t)
   of nkSym:
     let field = n.sym
     if field.typ.kind == tyVoid: return
-    if field.loc.r == "": fillObjectFields(p.module, typ)
+    if field.loc.snippet == "": fillObjectFields(p.module, typ)
     if field.loc.t == nil:
       internalError(p.config, n.info, "specializeResetN()")
-    specializeResetT(p, "$1.$2" % [accessor, field.loc.r], field.loc.t)
+    specializeResetT(p, "$1.$2" % [accessor, field.loc.snippet], field.loc.t)
   else: internalError(p.config, n.info, "specializeResetN()")
 
 proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
@@ -59,8 +59,8 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
     let arraySize = lengthOrd(p.config, typ.indexType)
     var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt))
     linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
-            [i.r, arraySize])
-    specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.r]), typ.elementType)
+            [i.snippet, arraySize])
+    specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
     lineF(p, cpsStmts, "}$n", [])
   of tyObject:
     var x = typ.baseClass
@@ -96,7 +96,7 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
       raiseAssert "unexpected set type kind"
   of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
       tyGenericParam, tyOrdinal, tyOpenArray, tyForward, tyVarargs,
-      tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
+      tyUncheckedArray, tyError, tyBuiltInTypeClass, tyUserTypeClass,
       tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot,
       tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable:
     discard
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 345639d94..883108f2c 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -110,10 +110,10 @@ proc genVarTuple(p: BProc, n: PNode) =
       initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[^1]))
     var field = initLoc(locExpr, vn, tup.storage)
     if t.kind == tyTuple:
-      field.r = "$1.Field$2" % [rdLoc(tup), rope(i)]
+      field.snippet = "$1.Field$2" % [rdLoc(tup), rope(i)]
     else:
       if t.n[i].kind != nkSym: internalError(p.config, n.info, "genVarTuple")
-      field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)]
+      field.snippet = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)]
     putLocIntoDest(p, v.loc, field)
     if forHcr or isGlobalInBlock:
       hcrGlobals.add((loc: v.loc, tp: "NULL"))
@@ -128,7 +128,7 @@ proc genVarTuple(p: BProc, n: PNode) =
     lineCg(p, cpsLocals, "NIM_BOOL $1 = NIM_FALSE;$n", [hcrCond])
     for curr in hcrGlobals:
       lineCg(p, cpsLocals, "$1 |= hcrRegisterGlobal($4, \"$2\", sizeof($3), $5, (void**)&$2);$N",
-              [hcrCond, curr.loc.r, rdLoc(curr.loc), getModuleDllPath(p.module, n[0].sym), curr.tp])
+              [hcrCond, curr.loc.snippet, rdLoc(curr.loc), getModuleDllPath(p.module, n[0].sym), curr.tp])
 
 
 proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
@@ -267,11 +267,11 @@ proc genBreakState(p: BProc, n: PNode, d: var TLoc) =
 
   if n[0].kind == nkClosure:
     a = initLocExpr(p, n[0][1])
-    d.r = "(((NI*) $1)[1] < 0)" % [rdLoc(a)]
+    d.snippet = "(((NI*) $1)[1] < 0)" % [rdLoc(a)]
   else:
     a = initLocExpr(p, n[0])
     # the environment is guaranteed to contain the 'state' field at offset 1:
-    d.r = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)]
+    d.snippet = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)]
 
 proc genGotoVar(p: BProc; value: PNode) =
   if value.kind notin {nkCharLit..nkUInt64Lit}:
@@ -289,7 +289,7 @@ proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) =
     #echo "New code produced for ", v.name.s, " ", p.config $ value.info
     genBracedInit(p, value, isConst = false, v.typ, result)
 
-proc genCppParamsForCtor(p: BProc; call: PNode): string =
+proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): string =
   result = ""
   var argsCounter = 0
   let typ = skipTypes(call[0].typ, abstractInst)
@@ -298,12 +298,23 @@ proc genCppParamsForCtor(p: BProc; call: PNode): string =
     #if it's a type we can just generate here another initializer as we are in an initializer context
     if call[i].kind == nkCall and call[i][0].kind == nkSym and call[i][0].sym.kind == skType:
       if argsCounter > 0: result.add ","
-      result.add genCppInitializer(p.module, p, call[i][0].sym.typ)
+      result.add genCppInitializer(p.module, p, call[i][0].sym.typ, didGenTemp)
     else:
+      #We need to test for temp in globals, see: #23657
+      let param =
+        if typ[i].kind in {tyVar} and call[i].kind == nkHiddenAddr:
+          call[i][0]
+        else:
+          call[i]
+      if param.kind != nkBracketExpr or param.typ.kind in
+        {tyRef, tyPtr, tyUncheckedArray, tyArray, tyOpenArray,
+          tyVarargs, tySequence, tyString, tyCstring, tyTuple}:
+        let tempLoc = initLocExprSingleUse(p, param)
+        didGenTemp = didGenTemp or tempLoc.k == locTemp
       genOtherArg(p, call, i, typ, result, argsCounter)
 
-proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope) =
-  let params = genCppParamsForCtor(p, call)
+proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope, didGenTemp: var bool) =
+  let params = genCppParamsForCtor(p, call, didGenTemp)
   if params.len == 0:
     decl = runtimeFormat("$#;\n", [decl])
   else:
@@ -330,7 +341,14 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
       # v.owner.kind != skModule:
       targetProc = p.module.preInitProc
     if isCppCtorCall and not containsHiddenPointer(v.typ):
-      callGlobalVarCppCtor(targetProc, v, vn, value)
+      var didGenTemp = false
+      callGlobalVarCppCtor(targetProc, v, vn, value, didGenTemp)
+      if didGenTemp:
+        message(p.config, vn.info, warnGlobalVarConstructorTemporary, vn.sym.name.s)
+        #We fail to call the constructor in the global scope so we do the call inside the main proc
+        assignGlobalVar(targetProc, vn, valueAsRope)
+        var loc = initLocExprSingleUse(targetProc, value)
+        genAssignment(targetProc, v.loc, loc, {})
     else:
       assignGlobalVar(targetProc, vn, valueAsRope)
 
@@ -365,7 +383,8 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
       var decl = localVarDecl(p, vn)
       var tmp: TLoc
       if isCppCtorCall:
-        genCppVarForCtor(p, value, decl)
+        var didGenTemp = false
+        genCppVarForCtor(p, value, decl, didGenTemp)
         line(p, cpsStmts, decl)
       else:
         tmp = initLocExprSingleUse(p, value)
@@ -386,7 +405,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
     # put it in the locals section - mainly because of loops which
     # use the var in a call to resetLoc() in the statements section
     lineCg(targetProc, cpsLocals, "hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1);$n",
-           [v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
+           [v.loc.snippet, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
     # nothing special left to do later on - let's avoid closing and reopening blocks
     forHcr = false
 
@@ -395,7 +414,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
   # be able to re-run it but without the top level code - just the init of globals
   if forHcr:
     lineCg(targetProc, cpsStmts, "if (hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1))$N",
-           [v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
+           [v.loc.snippet, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
     startBlock(targetProc)
   if value.kind != nkEmpty and valueAsRope.len == 0:
     genLineDir(targetProc, vn)
@@ -736,6 +755,18 @@ proc raiseExit(p: BProc) =
       lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto LA$1_;$n",
         [p.nestedTryStmts[^1].label])
 
+proc raiseExitCleanup(p: BProc, destroy: string) =
+  assert p.config.exc == excGoto
+  if nimErrorFlagDisabled notin p.flags:
+    p.flags.incl nimErrorFlagAccessed
+    if p.nestedTryStmts.len == 0:
+      p.flags.incl beforeRetNeeded
+      # easy case, simply goto 'ret':
+      lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) {$1; goto BeforeRet_;}$n", [destroy])
+    else:
+      lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) {$2; goto LA$1_;}$n",
+        [p.nestedTryStmts[^1].label, destroy])
+
 proc finallyActions(p: BProc) =
   if p.config.exc != excGoto and p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept:
     # if the current try stmt have a finally block,
@@ -765,10 +796,14 @@ proc genRaiseStmt(p: BProc, t: PNode) =
     var e = rdLoc(a)
     discard getTypeDesc(p.module, t[0].typ)
     var typ = skipTypes(t[0].typ, abstractPtrs)
-    # XXX For reasons that currently escape me, this is only required by the new
-    # C++ based exception handling:
-    if p.config.exc == excCpp:
+    case p.config.exc
+    of excCpp:
       blockLeaveActions(p, howManyTrys = 0, howManyExcepts = p.inExceptBlockLen)
+    of excGoto:
+      blockLeaveActions(p, howManyTrys = 0,
+        howManyExcepts = (if p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept: 1 else: 0))
+    else:
+      discard
     genLineDir(p, t)
     if isImportedException(typ, p.config):
       lineF(p, cpsStmts, "throw $1;$n", [e])
@@ -1510,7 +1545,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) =
       else:
         discard getTypeDesc(p.module, skipTypes(sym.typ, abstractPtrs))
         fillBackendName(p.module, sym)
-        res.add($sym.loc.r)
+        res.add($sym.loc.snippet)
     of nkTypeOfExpr:
       res.add($getTypeDesc(p.module, it.typ))
     else:
@@ -1547,14 +1582,14 @@ proc genAsmStmt(p: BProc, t: PNode) =
       if whichPragma(i) == wAsmSyntax:
         asmSyntax = i[1].strVal
 
-  if asmSyntax != "" and 
+  if asmSyntax != "" and
      not (
       asmSyntax == "gcc" and hasGnuAsm in CC[p.config.cCompiler].props or
       asmSyntax == "vcc" and hasGnuAsm notin CC[p.config.cCompiler].props):
     localError(
-      p.config, t.info, 
+      p.config, t.info,
       "Your compiler does not support the specified inline assembler")
-  
+
   genAsmOrEmitStmt(p, t, isAsmStmt=true, s)
   # see bug #2362, "top level asm statements" seem to be a mis-feature
   # but even if we don't do this, the example in #2362 cannot possibly
@@ -1592,9 +1627,9 @@ proc genPragma(p: BProc, n: PNode) =
     case whichPragma(it)
     of wEmit: genEmit(p, it)
     of wPush:
-      processPushBackendOption(p.optionsStack, p.options, n, i+1)
+      processPushBackendOption(p.config, p.optionsStack, p.options, n, i+1)
     of wPop:
-      processPopBackendOption(p.optionsStack, p.options)
+      processPopBackendOption(p.config, p.optionsStack, p.options)
     else: discard
 
 
diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim
index abf830b57..1f551f022 100644
--- a/compiler/ccgthreadvars.nim
+++ b/compiler/ccgthreadvars.nim
@@ -30,7 +30,7 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
     # allocator for it :-(
     if not containsOrIncl(m.g.nimtvDeclared, s.id):
       m.g.nimtvDeps.add(s.loc.t)
-      m.g.nimtv.addf("$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
+      m.g.nimtv.addf("$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.snippet])
   else:
     if isExtern: m.s[cfsVars].add("extern ")
     elif lfExportLib in s.loc.flags: m.s[cfsVars].add("N_LIB_EXPORT_VAR ")
@@ -41,7 +41,7 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
         m.s[cfsVars].add("NIM_THREAD_LOCAL ")
       else: m.s[cfsVars].add("NIM_THREADVAR ")
     m.s[cfsVars].add(getTypeDesc(m, s.loc.t))
-    m.s[cfsVars].addf(" $1;$n", [s.loc.r])
+    m.s[cfsVars].addf(" $1;$n", [s.loc.snippet])
 
 proc generateThreadLocalStorage(m: BModule) =
   if m.g.nimtv != "" and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim
index 3fd269bc6..ed4c79d9a 100644
--- a/compiler/ccgtrav.nim
+++ b/compiler/ccgtrav.nim
@@ -34,10 +34,10 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
     if (n[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
     var p = c.p
     let disc = n[0].sym
-    if disc.loc.r == "": fillObjectFields(c.p.module, typ)
+    if disc.loc.snippet == "": fillObjectFields(c.p.module, typ)
     if disc.loc.t == nil:
       internalError(c.p.config, n.info, "genTraverseProc()")
-    lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
+    lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.snippet])
     for i in 1..<n.len:
       let branch = n[i]
       assert branch.kind in {nkOfBranch, nkElse}
@@ -51,10 +51,10 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
   of nkSym:
     let field = n.sym
     if field.typ.kind == tyVoid: return
-    if field.loc.r == "": fillObjectFields(c.p.module, typ)
+    if field.loc.snippet == "": fillObjectFields(c.p.module, typ)
     if field.loc.t == nil:
       internalError(c.p.config, n.info, "genTraverseProc()")
-    genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
+    genTraverseProc(c, "$1.$2" % [accessor, field.loc.snippet], field.loc.t)
   else: internalError(c.p.config, n.info, "genTraverseProc()")
 
 proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
@@ -78,9 +78,9 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
     var oldCode = p.s(cpsStmts)
     freeze oldCode
     linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
-            [i.r, arraySize])
+            [i.snippet, arraySize])
     let oldLen = p.s(cpsStmts).len
-    genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.r]), typ.elementType)
+    genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
     if p.s(cpsStmts).len == oldLen:
       # do not emit dummy long loops for faster debug builds:
       p.s(cpsStmts) = oldCode
@@ -120,12 +120,12 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
   var i = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
   var oldCode = p.s(cpsStmts)
   freeze oldCode
-  var a = TLoc(r: accessor)
+  var a = TLoc(snippet: accessor)
 
   lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
-      [i.r, lenExpr(c.p, a)])
+      [i.snippet, lenExpr(c.p, a)])
   let oldLen = p.s(cpsStmts).len
-  genTraverseProc(c, "$1$3[$2]" % [accessor, i.r, dataField(c.p)], typ.elementType)
+  genTraverseProc(c, "$1$3[$2]" % [accessor, i.snippet, dataField(c.p)], typ.elementType)
   if p.s(cpsStmts).len == oldLen:
     # do not emit dummy long loops for faster debug builds:
     p.s(cpsStmts) = oldCode
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 1366caab3..2c2556336 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -55,24 +55,24 @@ proc mangleField(m: BModule; name: PIdent): string =
   if isKeyword(name):
     result.add "_0"
 
-proc mangleProc(m: BModule; s: PSym; makeUnique: bool): string = 
+proc mangleProc(m: BModule; s: PSym; makeUnique: bool): string =
   result = "_Z"  # Common prefix in Itanium ABI
   result.add encodeSym(m, s, makeUnique)
   if s.typ.len > 1: #we dont care about the return param
-    for i in 1..<s.typ.len: 
+    for i in 1..<s.typ.len:
       if s.typ[i].isNil: continue
       result.add encodeType(m, s.typ[i])
-  
+
   if result in m.g.mangledPrcs:
     result = mangleProc(m, s, true)
   else:
     m.g.mangledPrcs.incl(result)
 
 proc fillBackendName(m: BModule; s: PSym) =
-  if s.loc.r == "":
+  if s.loc.snippet == "":
     var result: Rope
     if not m.compileToCpp and s.kind in routineKinds and optCDebug in m.g.config.globalOptions and
-      m.g.config.symbolFiles == disabledSf: 
+      m.g.config.symbolFiles == disabledSf:
       result = mangleProc(m, s, false).rope
     else:
       result = s.name.s.mangle.rope
@@ -80,11 +80,11 @@ proc fillBackendName(m: BModule; s: PSym) =
     if m.hcrOn:
       result.add '_'
       result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts, m.config))
-    s.loc.r = result
+    s.loc.snippet = result
     writeMangledName(m.ndi, s, m.config)
 
 proc fillParamName(m: BModule; s: PSym) =
-  if s.loc.r == "":
+  if s.loc.snippet == "":
     var res = s.name.s.mangle
     res.add mangleParamExt(s)
     #res.add idOrSig(s, res, m.sigConflicts, m.config)
@@ -104,13 +104,13 @@ proc fillParamName(m: BModule; s: PSym) =
     # and a function called in main or proxy uses `socket` as a parameter name.
     # That would lead to either needing to reload `proxy` or to overwrite the
     # executable file for the main module, which is running (or both!) -> error.
-    s.loc.r = res.rope
+    s.loc.snippet = res.rope
     writeMangledName(m.ndi, s, m.config)
 
 proc fillLocalName(p: BProc; s: PSym) =
   assert s.kind in skLocalVars+{skTemp}
   #assert sfGlobal notin s.flags
-  if s.loc.r == "":
+  if s.loc.snippet == "":
     var key = s.name.s.mangle
     let counter = p.sigConflicts.getOrDefault(key)
     var result = key.rope
@@ -120,7 +120,7 @@ proc fillLocalName(p: BProc; s: PSym) =
     elif counter != 0 or isKeyword(s.name) or p.module.g.config.cppDefines.contains(key):
       result.add "_" & rope(counter+1)
     p.sigConflicts.inc(key)
-    s.loc.r = result
+    s.loc.snippet = result
     if s.kind != skTemp: writeMangledName(p.module.ndi, s, p.config)
 
 proc scopeMangledParam(p: BProc; param: PSym) =
@@ -147,23 +147,23 @@ proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
   var t = typ
   while true:
     if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}:
-      return t.sym.loc.r
+      return t.sym.loc.snippet
 
     if t.kind in irrelevantForBackend:
       t = t.skipModifier
     else:
       break
   let typ = if typ.kind in {tyAlias, tySink, tyOwned}: typ.elementType else: typ
-  if typ.loc.r == "":
-    typ.typeName(typ.loc.r)
-    typ.loc.r.add $sig
+  if typ.loc.snippet == "":
+    typ.typeName(typ.loc.snippet)
+    typ.loc.snippet.add $sig
   else:
     when defined(debugSigHashes):
       # check consistency:
       var tn = newRopeAppender()
       typ.typeName(tn)
-      assert($typ.loc.r == $(tn & $sig))
-  result = typ.loc.r
+      assert($typ.loc.snippet == $(tn & $sig))
+  result = typ.loc.snippet
   if result == "": internalError(m.config, "getTypeName: " & $typ.kind)
 
 proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
@@ -189,7 +189,7 @@ proc mapType(conf: ConfigRef; typ: PType; isParam: bool): TCTypeKind =
   of tyObject, tyTuple: result = ctStruct
   of tyUserTypeClasses:
     doAssert typ.isResolvedUserTypeClass
-    return mapType(conf, typ.skipModifier, isParam)
+    result = mapType(conf, typ.skipModifier, isParam)
   of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal,
      tyTypeDesc, tyAlias, tySink, tyInferred, tyOwned:
     result = mapType(conf, skipModifier(typ), isParam)
@@ -277,9 +277,12 @@ proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool =
       if rettype.isImportedCppType or t.isImportedCppType or
           (typ.callConv == ccCDecl and conf.selectedGC in {gcArc, gcAtomicArc, gcOrc}):
         # prevents nrvo for cdecl procs; # bug #23401
-        return false
-      result = containsGarbageCollectedRef(t) or
-          (t.kind == tyObject and not isObjLackingTypeField(t))
+        result = false
+      else:
+        result = containsGarbageCollectedRef(t) or
+            (t.kind == tyObject and not isObjLackingTypeField(t)) or
+            (getSize(conf, rettype) == szUnknownSize and (t.sym == nil or sfImportc notin t.sym.flags))
+
     else: result = false
 
 const
@@ -287,7 +290,7 @@ const
     "N_STDCALL", "N_CDECL", "N_SAFECALL",
     "N_SYSCALL", # this is probably not correct for all platforms,
                  # but one can #define it to what one wants
-    "N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_THISCALL", "N_CLOSURE", "N_NOCONV", 
+    "N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_THISCALL", "N_CLOSURE", "N_NOCONV",
     "N_NOCONV" #ccMember is N_NOCONV
     ]
 
@@ -318,7 +321,7 @@ proc fillResult(conf: ConfigRef; param: PNode, proctype: PType) =
 proc typeNameOrLiteral(m: BModule; t: PType, literal: string): Rope =
   if t.sym != nil and sfImportc in t.sym.flags and t.sym.magic == mNone:
     useHeader(m, t.sym)
-    result = t.sym.loc.r
+    result = t.sym.loc.snippet
   else:
     result = rope(literal)
 
@@ -373,13 +376,6 @@ proc getTypePre(m: BModule; typ: PType; sig: SigHash): Rope =
     result = getSimpleTypeDesc(m, typ)
     if result == "": result = cacheGetType(m.typeCache, sig)
 
-proc structOrUnion(t: PType): Rope =
-  let cachedUnion = rope("union")
-  let cachedStruct = rope("struct")
-  let t = t.skipTypes({tyAlias, tySink})
-  if tfUnion in t.flags: cachedUnion
-  else: cachedStruct
-
 proc addForwardStructFormat(m: BModule; structOrUnion: Rope, typename: Rope) =
   if m.compileToCpp:
     m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename]
@@ -478,8 +474,8 @@ macro unrollChars(x: static openArray[char], name, body: untyped) =
       copy body
     )))
 
-proc multiFormat*(frmt: var string, chars : static openArray[char], args: openArray[seq[string]]) =
-  var res : string
+proc multiFormat*(frmt: var string, chars: static openArray[char], args: openArray[seq[string]]) =
+  var res: string
   unrollChars(chars, c):
     res = ""
     let arg = args[find(chars, c)]
@@ -521,7 +517,8 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params
                    weakDep=false;) =
   let t = prc.typ
   let isCtor = sfConstructor in prc.flags
-  if isCtor or (name[0] == '~' and sfMember in prc.flags): #destructors cant have void
+  if isCtor or (name[0] == '~' and sfMember in prc.flags):
+    # destructors can't have void
     rettype = ""
   elif t.returnType == nil or isInvalidReturnType(m.config, t):
     rettype = "void"
@@ -537,10 +534,10 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params
     fillLoc(this.loc, locParam, t.n[1],
             this.paramStorageLoc)
     if this.typ.kind == tyPtr:
-      this.loc.r = "this"
+      this.loc.snippet = "this"
     else:
-      this.loc.r = "(*this)"
-    names.add this.loc.r
+      this.loc.snippet = "(*this)"
+    names.add this.loc.snippet
     types.add getTypeDescWeak(m, this.typ, check, dkParam)
 
   let firstParam = if isCtor: 1 else: 2
@@ -553,7 +550,7 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params
         descKind = dkRefGenericParam
       else:
         descKind = dkRefParam
-    var typ, name : string
+    var typ, name: string
     fillParamName(m, param)
     fillLoc(param.loc, locParam, t.n[i],
             param.paramStorageLoc)
@@ -568,7 +565,7 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params
     if sfNoalias in param.flags:
       typ.add("NIM_NOALIAS ")
 
-    name = param.loc.r
+    name = param.loc.snippet
     types.add typ
     names.add name
     if sfCodegenDecl notin param.flags:
@@ -628,9 +625,9 @@ proc genProcParams(m: BModule; t: PType, rettype, params: var Rope,
       typ.add("NIM_NOALIAS ")
     if sfCodegenDecl notin param.flags:
       params.add(typ)
-      params.add(param.loc.r)
+      params.add(param.loc.snippet)
     else:
-      params.add runtimeFormat(param.cgDeclFrmt, [typ, param.loc.r])
+      params.add runtimeFormat(param.cgDeclFrmt, [typ, param.loc.snippet])
     # declare the len field for open arrays:
     var arr = param.typ.skipTypes({tyGenericInst})
     if arr.kind in {tyVar, tyLent, tySink}: arr = arr.elementType
@@ -639,7 +636,7 @@ proc genProcParams(m: BModule; t: PType, rettype, params: var Rope,
       # this fixes the 'sort' bug:
       if param.typ.kind in {tyVar, tyLent}: param.loc.storage = OnUnknown
       # need to pass hidden parameter:
-      params.addf(", NI $1Len_$2", [param.loc.r, j.rope])
+      params.addf(", NI $1Len_$2", [param.loc.snippet, j.rope])
       inc(j)
       arr = arr[0].skipTypes({tySink})
   if t.returnType != nil and isInvalidReturnType(m.config, t):
@@ -667,7 +664,7 @@ proc genProcParams(m: BModule; t: PType, rettype, params: var Rope,
 
 proc mangleRecFieldName(m: BModule; field: PSym): Rope =
   if {sfImportc, sfExportc} * field.flags != {}:
-    result = field.loc.r
+    result = field.loc.snippet
   else:
     result = rope(mangleField(m, field.name))
   if result == "": internalError(m.config, field.info, "mangleRecFieldName")
@@ -679,9 +676,9 @@ proc hasCppCtor(m: BModule; typ: PType): bool =
       if sfConstructor in prc.flags:
         return true
 
-proc genCppParamsForCtor(p: BProc; call: PNode): string
+proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): string
 
-proc genCppInitializer(m: BModule, prc: BProc; typ: PType): string =
+proc genCppInitializer(m: BModule, prc: BProc; typ: PType; didGenTemp: var bool): string =
   #To avoid creating a BProc per test when called inside a struct nil BProc is allowed
   result = "{}"
   if typ.itemId in m.g.graph.initializersPerType:
@@ -690,13 +687,13 @@ proc genCppInitializer(m: BModule, prc: BProc; typ: PType): string =
       var p = prc
       if p == nil:
         p = BProc(module: m)
-      result = "{" & genCppParamsForCtor(p, call) & "}"
+      result = "{" & genCppParamsForCtor(p, call, didGenTemp) & "}"
       if prc == nil:
         assert p.blocks.len == 0, "BProc belongs to a struct doesnt have blocks"
 
 proc genRecordFieldsAux(m: BModule; n: PNode,
                         rectype: PType,
-                        check: var IntSet; result: var Rope; unionPrefix = "") =
+                        check: var IntSet; result: var Builder; unionPrefix = "") =
   case n.kind
   of nkRecList:
     for i in 0..<n.len:
@@ -713,63 +710,51 @@ proc genRecordFieldsAux(m: BModule; n: PNode,
         let k = lastSon(n[i])
         if k.kind != nkSym:
           let structName = "_" & mangleRecFieldName(m, n[0].sym) & "_" & $i
-          var a = newRopeAppender()
+          var a = newBuilder("")
           genRecordFieldsAux(m, k, rectype, check, a, unionPrefix & $structName & ".")
-          if a != "":
-            if tfPacked notin rectype.flags:
-              unionBody.add("struct {")
-            else:
-              if hasAttribute in CC[m.config.cCompiler].props:
-                unionBody.add("struct __attribute__((__packed__)){")
-              else:
-                unionBody.addf("#pragma pack(push, 1)$nstruct{", [])
-            unionBody.add(a)
-            unionBody.addf("} $1;$n", [structName])
-            if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props:
-              unionBody.addf("#pragma pack(pop)$n", [])
+          if a.len != 0:
+            unionBody.addFieldWithStructType(m, rectype, structName):
+              unionBody.add(a)
         else:
           genRecordFieldsAux(m, k, rectype, check, unionBody, unionPrefix)
       else: internalError(m.config, "genRecordFieldsAux(record case branch)")
-    if unionBody != "":
-      result.addf("union{\n$1};$n", [unionBody])
+    if unionBody.len != 0:
+      result.addAnonUnion:
+        result.add(unionBody)
   of nkSym:
     let field = n.sym
     if field.typ.kind == tyVoid: return
     #assert(field.ast == nil)
     let sname = mangleRecFieldName(m, field)
     fillLoc(field.loc, locField, n, unionPrefix & sname, OnUnknown)
-    if field.alignment > 0:
-      result.addf "NIM_ALIGN($1) ", [rope(field.alignment)]
     # for importcpp'ed objects, we only need to set field.loc, but don't
     # have to recurse via 'getTypeDescAux'. And not doing so prevents problems
     # with heavily templatized C++ code:
     if not isImportedCppType(rectype):
-      let noAlias = if sfNoalias in field.flags: " NIM_NOALIAS" else: ""
-
       let fieldType = field.loc.lode.typ.skipTypes(abstractInst)
+      var typ: Rope = ""
+      var isFlexArray = false
+      var initializer = ""
       if fieldType.kind == tyUncheckedArray:
-        result.addf("\t$1 $2[SEQ_DECL_SIZE];$n",
-            [getTypeDescAux(m, fieldType.elemType, check, dkField), sname])
+        typ = getTypeDescAux(m, fieldType.elemType, check, dkField)
+        isFlexArray = true
       elif fieldType.kind == tySequence:
         # we need to use a weak dependency here for trecursive_table.
-        result.addf("\t$1$3 $2;$n", [getTypeDescWeak(m, field.loc.t, check, dkField), sname, noAlias])
-      elif field.bitsize != 0:
-        result.addf("\t$1$4 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, rope($field.bitsize), noAlias])
+        typ = getTypeDescWeak(m, field.loc.t, check, dkField)
       else:
+        typ = getTypeDescAux(m, field.loc.t, check, dkField)
         # don't use fieldType here because we need the
         # tyGenericInst for C++ template support
         let noInit = sfNoInit in field.flags or (field.typ.sym != nil and sfNoInit in field.typ.sym.flags)
         if not noInit and (fieldType.isOrHasImportedCppType() or hasCppCtor(m, field.owner.typ)):
-          var initializer = genCppInitializer(m, nil, fieldType)
-          result.addf("\t$1$3 $2$4;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias, initializer])
-        else:
-          result.addf("\t$1$3 $2;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias])
+          var didGenTemp = false
+          initializer = genCppInitializer(m, nil, fieldType, didGenTemp)
+      result.addField(field, sname, typ, isFlexArray, initializer)
   else: internalError(m.config, n.info, "genRecordFieldsAux()")
 
 proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl:bool = false)
 
-proc getRecordFields(m: BModule; typ: PType, check: var IntSet): Rope =
-  result = newRopeAppender()
+proc addRecordFields(result: var Builder; m: BModule; typ: PType, check: var IntSet) =
   genRecordFieldsAux(m, typ.n, typ, check, result)
   if typ.itemId in m.g.graph.memberProcsPerType:
     let procs = m.g.graph.memberProcsPerType[typ.itemId]
@@ -791,88 +776,34 @@ proc fillObjectFields*(m: BModule; typ: PType) =
   # sometimes generic objects are not consistently merged. We patch over
   # this fact here.
   var check = initIntSet()
-  discard getRecordFields(m, typ, check)
+  var ignored = newBuilder("")
+  addRecordFields(ignored, m, typ, check)
 
 proc mangleDynLibProc(sym: PSym): Rope
 
-proc getRecordDescAux(m: BModule; typ: PType, name, baseType: Rope,
-                   check: var IntSet, hasField:var bool): Rope =
-  result = ""
-  if typ.kind == tyObject:
-    if typ.baseClass == nil:
-      if lacksMTypeField(typ):
-        appcg(m, result, " {$n", [])
-      else:
-        if optTinyRtti in m.config.globalOptions:
-          appcg(m, result, " {$n#TNimTypeV2* m_type;$n", [])
-        else:
-          appcg(m, result, " {$n#TNimType* m_type;$n", [])
-        hasField = true
-    elif m.compileToCpp:
-      appcg(m, result, " : public $1 {$n", [baseType])
-      if typ.isException and m.config.exc == excCpp:
-        when false:
-          appcg(m, result, "virtual void raise() { throw *this; }$n", []) # required for polymorphic exceptions
-          if typ.sym.magic == mException:
-            # Add cleanup destructor to Exception base class
-            appcg(m, result, "~$1();$n", [name])
-            # define it out of the class body and into the procs section so we don't have to
-            # artificially forward-declare popCurrentExceptionEx (very VERY troublesome for HCR)
-            appcg(m, cfsProcs, "inline $1::~$1() {if(this->raiseId) #popCurrentExceptionEx(this->raiseId);}$n", [name])
-      hasField = true
-    else:
-      appcg(m, result, " {$n  $1 Sup;$n", [baseType])
-      hasField = true
-  else:
-    result.addf(" {$n", [name])
-
 proc getRecordDesc(m: BModule; typ: PType, name: Rope,
                    check: var IntSet): Rope =
   # declare the record:
-  var hasField = false
-  var structOrUnion: string
-  if tfPacked in typ.flags:
-      if hasAttribute in CC[m.config.cCompiler].props:
-        structOrUnion = structOrUnion(typ) & " __attribute__((__packed__))"
-      else:
-        structOrUnion = "#pragma pack(push, 1)\L" & structOrUnion(typ)
-  else:
-    structOrUnion = structOrUnion(typ)
   var baseType: string = ""
   if typ.baseClass != nil:
     baseType = getTypeDescAux(m, typ.baseClass.skipTypes(skipPtrs), check, dkField)
   if typ.sym == nil or sfCodegenDecl notin typ.sym.flags:
-    result = structOrUnion & " " & name
-    result.add(getRecordDescAux(m, typ, name, baseType, check, hasField))
-    let desc = getRecordFields(m, typ, check)
-    if not hasField and typ.itemId notin m.g.graph.memberProcsPerType:
-      if desc == "":
-        result.add("\tchar dummy;\n")
-      elif typ.n.len == 1 and typ.n[0].kind == nkSym:
-        let field = typ.n[0].sym
-        let fieldType = field.typ.skipTypes(abstractInst)
-        if fieldType.kind == tyUncheckedArray:
-          result.add("\tchar dummy;\n")
-      result.add(desc)
-    else:
-      result.add(desc)
-    result.add("};\L")
+    result = newBuilder("")
+    result.addStruct(m, typ, name, baseType):
+      result.addRecordFields(m, typ, check)
   else:
-    let desc = getRecordFields(m, typ, check)
+    var desc = newBuilder("")
+    desc.addRecordFields(m, typ, check)
     result = runtimeFormat(typ.sym.cgDeclFrmt, [name, desc, baseType])
-  if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props:
-    result.add "#pragma pack(pop)\L"
 
 proc getTupleDesc(m: BModule; typ: PType, name: Rope,
                   check: var IntSet): Rope =
-  result = "$1 $2 {$n" % [structOrUnion(typ), name]
-  var desc: Rope = ""
-  for i, a in typ.ikids:
-    desc.addf("$1 Field$2;$n",
-         [getTypeDescAux(m, a, check, dkField), rope(i)])
-  if desc == "": result.add("char dummy;\L")
-  else: result.add(desc)
-  result.add("};\L")
+  result = newBuilder("")
+  result.addStruct(m, typ, name, ""):
+    for i, a in typ.ikids:
+      result.addField(
+        name = "Field" & $i,
+        typ = getTypeDescAux(m, a, check, dkField))
 
 proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool =
   # A helper proc for handling cppimport patterns, involving numeric
@@ -929,12 +860,11 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
   if t != origTyp and origTyp.sym != nil: useHeader(m, origTyp.sym)
   let sig = hashType(origTyp, m.config)
 
-  result = "" # todo move `result = getTypePre(m, t, sig)` here ?
+  result = getTypePre(m, t, sig)
   defer: # defer is the simplest in this case
     if isImportedType(t) and not m.typeABICache.containsOrIncl(sig):
       addAbiCheck(m, t, result)
 
-  result = getTypePre(m, t, sig)
   if result != "" and t.kind != tyOpenArray:
     excl(check, t.id)
     if kind == dkRefParam or kind == dkRefGenericParam and origTyp.kind == tyGenericInst:
@@ -1037,18 +967,14 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
       m.typeCache[sig] = result & seqStar(m)
       if not isImportedType(t):
         if skipTypes(t.elementType, typedescInst).kind != tyEmpty:
-          const
-            cppSeq = "struct $2 : #TGenericSeq {$n"
-            cSeq = "struct $2 {$n" &
-                  "  #TGenericSeq Sup;$n"
-          if m.compileToCpp:
-            appcg(m, m.s[cfsSeqTypes],
-                cppSeq & "  $1 data[SEQ_DECL_SIZE];$n" &
-                "};$n", [getTypeDescAux(m, t.elementType, check, kind), result])
-          else:
-            appcg(m, m.s[cfsSeqTypes],
-                cSeq & "  $1 data[SEQ_DECL_SIZE];$n" &
-                "};$n", [getTypeDescAux(m, t.elementType, check, kind), result])
+          var struct = newBuilder("")
+          let baseType = cgsymValue(m, "TGenericSeq")
+          struct.addSimpleStruct(m, name = result, baseType = baseType):
+            struct.addField(
+              name = "data",
+              typ = getTypeDescAux(m, t.elementType, check, kind),
+              isFlexArray = true)
+          m.s[cfsSeqTypes].add struct
         else:
           result = rope("TGenericSeq")
       result.add(seqStar(m))
@@ -1220,7 +1146,7 @@ proc parseVFunctionDecl(val: string; name, params, retType, superCall: var strin
 
   params = "(" & params & ")"
 
-proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl : bool = false) =
+proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl: bool = false) =
   assert sfCppMember * prc.flags != {}
   let isCtor = sfConstructor in prc.flags
   var check = initIntSet()
@@ -1257,7 +1183,7 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool =
     superCall = ""
   else:
     if not isCtor:
-      prc.loc.r = "$1$2(@)" % [memberOp, name]
+      prc.loc.snippet = "$1$2(@)" % [memberOp, name]
     elif superCall != "":
       superCall = " : " & superCall
 
@@ -1278,7 +1204,7 @@ proc genProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false)
   # handle the 2 options for hotcodereloading codegen - function pointer
   # (instead of forward declaration) or header for function body with "_actual" postfix
   let asPtrStr = rope(if asPtr: "_PTR" else: "")
-  var name = prc.loc.r
+  var name = prc.loc.snippet
   if not asPtr and isReloadable(m, prc):
     name.add("_actual")
   # careful here! don't access ``prc.ast`` as that could reload large parts of
@@ -1418,13 +1344,13 @@ proc genObjectFields(m: BModule; typ, origType: PType, n: PNode, expr: Rope;
     var tmp = discriminatorTableName(m, typ, field)
     var L = lengthOrd(m.config, field.typ)
     assert L > 0
-    if field.loc.r == "": fillObjectFields(m, typ)
+    if field.loc.snippet == "": fillObjectFields(m, typ)
     if field.loc.t == nil:
       internalError(m.config, n.info, "genObjectFields")
     m.s[cfsTypeInit3].addf("$1.kind = 3;$n" &
         "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
         "$1.name = $5;$n" & "$1.sons = &$6[0];$n" &
-        "$1.len = $7;$n", [expr, getTypeDesc(m, origType, dkVar), field.loc.r,
+        "$1.len = $7;$n", [expr, getTypeDesc(m, origType, dkVar), field.loc.snippet,
                            genTypeInfoV1(m, field.typ, info),
                            makeCString(field.name.s),
                            tmp, rope(L)])
@@ -1456,13 +1382,13 @@ proc genObjectFields(m: BModule; typ, origType: PType, n: PNode, expr: Rope;
     # Do not produce code for void types
     if isEmptyType(field.typ): return
     if field.bitsize == 0:
-      if field.loc.r == "": fillObjectFields(m, typ)
+      if field.loc.snippet == "": fillObjectFields(m, typ)
       if field.loc.t == nil:
         internalError(m.config, n.info, "genObjectFields")
       m.s[cfsTypeInit3].addf("$1.kind = 1;$n" &
           "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
           "$1.name = $5;$n", [expr, getTypeDesc(m, origType, dkVar),
-          field.loc.r, genTypeInfoV1(m, field.typ, info), makeCString(field.name.s)])
+          field.loc.snippet, genTypeInfoV1(m, field.typ, info), makeCString(field.name.s)])
   else: internalError(m.config, n.info, "genObjectFields")
 
 proc genObjectInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) =
@@ -1472,7 +1398,7 @@ proc genObjectInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo
                       typeToString(typ))
   genTypeInfoAux(m, typ, origType, name, info)
   var tmp = getNimNode(m)
-  if not isImportedType(typ):
+  if (not isImportedType(typ)) or tfCompleteStruct in typ.flags:
     genObjectFields(m, typ, origType, typ.n, tmp, info)
   m.s[cfsTypeInit3].addf("$1.node = &$2;$n", [tiNameForHcr(m, name), tmp])
   var t = typ.baseClass
@@ -1567,7 +1493,7 @@ include ccgtrav
 proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
   genProc(m, s)
   m.s[cfsTypeInit3].addf("$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n",
-     [result, s.loc.r])
+     [result, s.loc.snippet])
 
 proc declareNimType(m: BModule; name: string; str: Rope, module: int) =
   let nr = rope(name)
@@ -1655,10 +1581,10 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result:
       let wrapper = generateRttiDestructor(m.g.graph, t, theProc.owner, attachedDestructor,
                 theProc.info, m.idgen, theProc)
       genProc(m, wrapper)
-      result.add wrapper.loc.r
+      result.add wrapper.loc.snippet
     else:
       genProc(m, theProc)
-      result.add theProc.loc.r
+      result.add theProc.loc.snippet
 
     when false:
       if not canFormAcycle(m.g.graph, t) and op == attachedTrace:
@@ -1706,7 +1632,7 @@ proc genVTable(seqs: seq[PSym]): string =
   result = "{"
   for i in 0..<seqs.len:
     if i > 0: result.add ", "
-    result.add "(void *) " & seqs[i].loc.r
+    result.add "(void *) " & seqs[i].loc.snippet
   result.add "}"
 
 proc genTypeInfoV2OldImpl(m: BModule; t, origType: PType, name: Rope; info: TLineInfo) =
@@ -1861,8 +1787,7 @@ proc typeToC(t: PType): string =
   ## to be unique.
   let s = typeToString(t)
   result = newStringOfCap(s.len)
-  for i in 0..<s.len:
-    let c = s[i]
+  for c in s:
     case c
     of 'a'..'z':
       result.add c
diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim
index da55df45d..c0e574186 100644
--- a/compiler/ccgutils.nim
+++ b/compiler/ccgutils.nim
@@ -90,6 +90,9 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
     if s.typ.sym != nil and sfForward in s.typ.sym.flags:
       # forwarded objects are *always* passed by pointers for consistency!
       result = true
+    elif s.typ.kind == tySink and conf.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcHooks}:
+      # bug #23354:
+      result = false
     elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3):
       result = true           # requested anyway
     elif (tfFinal in pt.flags) and (pt[0] == nil):
@@ -122,7 +125,7 @@ proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false): string =
   var name = s.name.s
   if makeUnique:
     name = makeUnique(m, s, name)
-  "N" & encodeName(s.owner.name.s) & encodeName(name) & "E"
+  "N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E"
 
 proc encodeType*(m: BModule; t: PType): string =
   result = ""
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index e4ba41614..091f5c842 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -15,7 +15,7 @@ import
   ccgutils, ropes, wordrecg, treetab, cgmeth,
   rodutils, renderer, cgendata, aliases,
   lowerings, ndi, lineinfos, pathutils, transf,
-  injectdestructors, astmsgs, modulepaths, backendpragmas,
+  injectdestructors, astmsgs, modulepaths, pushpoppragmas,
   mangleutils
 
 from expanddefaults import caseObjDefaultBranch
@@ -33,6 +33,12 @@ import std/strutils except `%`, addf # collides with ropes.`%`
 from ic / ic import ModuleBackendFlag
 import std/[dynlib, math, tables, sets, os, intsets, hashes]
 
+const
+  # we use some ASCII control characters to insert directives that will be converted to real code in a postprocessing pass
+  postprocessDirStart = '\1'
+  postprocessDirSep = '\31'
+  postprocessDirEnd = '\23'
+
 when not declared(dynlib.libCandidates):
   proc libCandidates(s: string, dest: var seq[string]) =
     ## given a library name pattern `s` write possible library names to `dest`.
@@ -66,8 +72,7 @@ proc findPendingModule(m: BModule, s: PSym): BModule =
 
 proc initLoc(k: TLocKind, lode: PNode, s: TStorageLoc, flags: TLocFlags = {}): TLoc =
   result = TLoc(k: k, storage: s, lode: lode,
-               r: "", flags: flags
-  )
+                snippet: "", flags: flags)
 
 proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) {.inline.} =
   # fills the loc if it is not already initialized
@@ -75,7 +80,7 @@ proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) {.i
     a.k = k
     a.lode = lode
     a.storage = s
-    if a.r == "": a.r = r
+    if a.snippet == "": a.snippet = r
 
 proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) {.inline.} =
   # fills the loc if it is not already initialized
@@ -267,24 +272,28 @@ proc safeLineNm(info: TLineInfo): int =
   result = toLinenumber(info)
   if result < 0: result = 0 # negative numbers are not allowed in #line
 
-proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) =
+proc genPostprocessDir(field1, field2, field3: string): string =
+  result = postprocessDirStart & field1 & postprocessDirSep & field2 & postprocessDirSep & field3 & postprocessDirEnd
+
+proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; conf: ConfigRef) =
   assert line >= 0
   if optLineDir in conf.options and line > 0:
-    r.addf("\n#line $2 $1\n",
-        [rope(makeSingleLineCString(filename)), rope(line)])
+    if fileIdx == InvalidFileIdx:
+      r.add(rope("\n#line " & $line & " \"generated_not_to_break_here\"\n"))
+    else:
+      r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n"))
 
-proc genCLineDir(r: var Rope, filename: string, line: int; p: BProc; info: TLineInfo; lastFileIndex: FileIndex) =
+proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; p: BProc; info: TLineInfo; lastFileIndex: FileIndex) =
   assert line >= 0
   if optLineDir in p.config.options and line > 0:
-    if lastFileIndex == info.fileIndex:
-        r.addf("\n#line $1\n", [rope(line)])
+    if fileIdx == InvalidFileIdx:
+      r.add(rope("\n#line " & $line & " \"generated_not_to_break_here\"\n"))
     else:
-      r.addf("\n#line $2 $1\n",
-        [rope(makeSingleLineCString(filename)), rope(line)])
+      r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n"))
 
 proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) =
   if optLineDir in conf.options:
-    genCLineDir(r, toFullPath(conf, info), info.safeLineNm, conf)
+    genCLineDir(r, info.fileIndex, info.safeLineNm, conf)
 
 proc freshLineInfo(p: BProc; info: TLineInfo): bool =
   if p.lastLineInfo.line != info.line or
@@ -299,7 +308,7 @@ proc genCLineDir(r: var Rope, p: BProc, info: TLineInfo; conf: ConfigRef) =
   if optLineDir in conf.options:
     let lastFileIndex = p.lastLineInfo.fileIndex
     if freshLineInfo(p, info):
-      genCLineDir(r, toFullPath(conf, info), info.safeLineNm, p, info, lastFileIndex)
+      genCLineDir(r, info.fileIndex, info.safeLineNm, p, info, lastFileIndex)
 
 proc genLineDir(p: BProc, t: PNode) =
   if p == p.module.preInitProc: return
@@ -310,16 +319,11 @@ proc genLineDir(p: BProc, t: PNode) =
   let lastFileIndex = p.lastLineInfo.fileIndex
   let freshLine = freshLineInfo(p, t.info)
   if freshLine:
-    genCLineDir(p.s(cpsStmts), toFullPath(p.config, t.info), line, p, t.info, lastFileIndex)
+    genCLineDir(p.s(cpsStmts), t.info.fileIndex, line, p, t.info, lastFileIndex)
   if ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and
       (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx:
       if freshLine:
-        if lastFileIndex == t.info.fileIndex:
-          linefmt(p, cpsStmts, "nimln_($1);",
-              [line])
-        else:
-          linefmt(p, cpsStmts, "nimlf_($1, $2);",
-              [line, quotedFilename(p.config, t.info)])
+        line(p, cpsStmts, genPostprocessDir("nimln", $line, $t.info.fileIndex.int32))
 
 proc accessThreadLocalVar(p: BProc, s: PSym)
 proc emulatedThreadVars(conf: ConfigRef): bool {.inline.}
@@ -336,15 +340,15 @@ proc getTempName(m: BModule): Rope =
 proc rdLoc(a: TLoc): Rope =
   # 'read' location (deref if indirect)
   if lfIndirect in a.flags:
-    result = "(*" & a.r & ")"
+    result = "(*" & a.snippet & ")"
   else:
-    result = a.r
+    result = a.snippet
 
 proc addRdLoc(a: TLoc; result: var Rope) =
   if lfIndirect in a.flags:
-    result.add "(*" & a.r & ")"
+    result.add "(*" & a.snippet & ")"
   else:
-    result.add a.r
+    result.add a.snippet
 
 proc lenField(p: BProc): Rope {.inline.} =
   result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
@@ -369,6 +373,7 @@ proc dataField(p: BProc): Rope =
 
 proc genProcPrototype(m: BModule, sym: PSym)
 
+include cbuilder
 include ccgliterals
 include ccgtypes
 
@@ -381,22 +386,22 @@ template mapTypeChooser(a: TLoc): TSymKind = mapTypeChooser(a.lode)
 
 proc addAddrLoc(conf: ConfigRef; a: TLoc; result: var Rope) =
   if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray:
-    result.add "(&" & a.r & ")"
+    result.add "(&" & a.snippet & ")"
   else:
-    result.add a.r
+    result.add a.snippet
 
 proc addrLoc(conf: ConfigRef; a: TLoc): Rope =
   if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray:
-    result = "(&" & a.r & ")"
+    result = "(&" & a.snippet & ")"
   else:
-    result = a.r
+    result = a.snippet
 
 proc byRefLoc(p: BProc; a: TLoc): Rope =
   if lfIndirect notin a.flags and mapType(p.config, a.t, mapTypeChooser(a) == skParam) != ctArray and not
       p.module.compileToCpp:
-    result = "(&" & a.r & ")"
+    result = "(&" & a.snippet & ")"
   else:
-    result = a.r
+    result = a.snippet
 
 proc rdCharLoc(a: TLoc): Rope =
   # read a location that may need a char-cast:
@@ -407,7 +412,9 @@ proc rdCharLoc(a: TLoc): Rope =
 type
   TAssignmentFlag = enum
     needToCopy
+    needToCopySinkParam
     needTempForOpenArray
+    needAssignCall
   TAssignmentFlags = set[TAssignmentFlag]
 
 proc genObjConstr(p: BProc, e: PNode, d: var TLoc)
@@ -476,9 +483,12 @@ include ccgreset
 proc resetLoc(p: BProc, loc: var TLoc) =
   let containsGcRef = optSeqDestructors notin p.config.globalOptions and containsGarbageCollectedRef(loc.t)
   let typ = skipTypes(loc.t, abstractVarRange)
-  if isImportedCppType(typ): return
+  if isImportedCppType(typ): 
+    var didGenTemp = false
+    linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(loc), genCppInitializer(p.module, p, typ, didGenTemp)])
+    return
   if optSeqDestructors in p.config.globalOptions and typ.kind in {tyString, tySequence}:
-    assert loc.r != ""
+    assert loc.snippet != ""
 
     let atyp = skipTypes(loc.t, abstractInst)
     if atyp.kind in {tyVar, tyLent}:
@@ -488,7 +498,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
   elif not isComplexValueType(typ):
     if containsGcRef:
       var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack)
-      nilLoc.r = rope("NIM_NIL")
+      nilLoc.snippet = rope("NIM_NIL")
       genRefAssign(p, loc, nilLoc)
     else:
       linefmt(p, cpsStmts, "$1 = 0;$n", [rdLoc(loc)])
@@ -526,7 +536,7 @@ proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) =
   elif not isComplexValueType(typ):
     if containsGarbageCollectedRef(loc.t):
       var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack)
-      nilLoc.r = rope("NIM_NIL")
+      nilLoc.snippet = rope("NIM_NIL")
       genRefAssign(p, loc, nilLoc)
     else:
       linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc),
@@ -554,13 +564,14 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
 
 proc getTemp(p: BProc, t: PType, needsInit=false): TLoc =
   inc(p.labels)
-  result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
+  result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
                 storage: OnStack, flags: {})
   if p.module.compileToCpp and isOrHasImportedCppType(t):
-    linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.r,
-      genCppInitializer(p.module, p, t)])
+    var didGenTemp = false
+    linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.snippet,
+      genCppInitializer(p.module, p, t, didGenTemp)])
   else:
-    linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.r])
+    linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.snippet])
   constructLoc(p, result, not needsInit)
   when false:
     # XXX Introduce a compiler switch in order to detect these easily.
@@ -573,16 +584,16 @@ proc getTemp(p: BProc, t: PType, needsInit=false): TLoc =
 
 proc getTempCpp(p: BProc, t: PType, value: Rope): TLoc =
   inc(p.labels)
-  result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
+  result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
                 storage: OnStack, flags: {})
-  linefmt(p, cpsStmts, "auto $1 = $2;$n", [result.r, value])
+  linefmt(p, cpsStmts, "auto $1 = $2;$n", [result.snippet, value])
 
 proc getIntTemp(p: BProc): TLoc =
   inc(p.labels)
-  result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp,
+  result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp,
                 storage: OnStack, lode: lodeTyp getSysType(p.module.g.graph, unknownLineInfo, tyInt),
                 flags: {})
-  linefmt(p, cpsLocals, "NI $1;$n", [result.r])
+  linefmt(p, cpsLocals, "NI $1;$n", [result.snippet])
 
 proc localVarDecl(p: BProc; n: PNode): Rope =
   result = ""
@@ -604,9 +615,9 @@ proc localVarDecl(p: BProc; n: PNode): Rope =
     if sfVolatile in s.flags: result.add(" volatile")
     if sfNoalias in s.flags: result.add(" NIM_NOALIAS")
     result.add(" ")
-    result.add(s.loc.r)
+    result.add(s.loc.snippet)
   else:
-    result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.r])
+    result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.snippet])
 
 proc assignLocalVar(p: BProc, n: PNode) =
   #assert(s.loc.k == locNone) # not yet assigned
@@ -615,7 +626,8 @@ proc assignLocalVar(p: BProc, n: PNode) =
   let nl = if optLineDir in p.config.options: "" else: "\n"
   var decl = localVarDecl(p, n)
   if p.module.compileToCpp and isOrHasImportedCppType(n.typ):
-    decl.add genCppInitializer(p.module, p, n.typ)
+    var didGenTemp = false
+    decl.add genCppInitializer(p.module, p, n.typ, didGenTemp)
   decl.add ";" & nl
   line(p, cpsLocals, decl)
 
@@ -646,22 +658,11 @@ proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) =
     if sfNoalias in s.flags: decl.add(" NIM_NOALIAS")
   else:
     if value != "":
-      decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.r, value])
+      decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.snippet, value])
     else:
-      decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r])
+      decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.snippet])
 
-proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope)
-
-proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode) =
-  let s = vn.sym
-  fillBackendName(p.module, s)
-  fillLoc(s.loc, locGlobalVar, vn, OnHeap)
-  var decl: Rope = ""
-  let td = getTypeDesc(p.module, vn.sym.typ, dkVar)
-  genGlobalVarDecl(p, vn, td, "", decl)
-  decl.add " " & $s.loc.r
-  genCppVarForCtor(p, value, decl)
-  p.module.s[cfsVars].add decl
+proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope; didGenTemp: var bool)
 
 proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
   let s = n.sym
@@ -675,7 +676,7 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
     if q != nil and not containsOrIncl(q.declaredThings, s.id):
       varInDynamicLib(q, s)
     else:
-      s.loc.r = mangleDynLibProc(s)
+      s.loc.snippet = mangleDynLibProc(s)
     if value != "":
       internalError(p.config, n.info, ".dynlib variables cannot have a value")
     return
@@ -706,19 +707,31 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
             # [^0]: https://en.cppreference.com/w/cpp/language/aggregate_initialization
             # [^1]: https://cplusplus.github.io/CWG/issues/1518.html
             # [^2]: https://eel.is/c++draft/over.match.ctor
-            decl.addf(" $1;$n", [s.loc.r])
+            decl.addf(" $1;$n", [s.loc.snippet])
           else:
-            decl.addf(" $1 = $2;$n", [s.loc.r, value])
+            decl.addf(" $1 = $2;$n", [s.loc.snippet, value])
         else:
-          decl.addf(" $1;$n", [s.loc.r])
+          decl.addf(" $1;$n", [s.loc.snippet])
 
       p.module.s[cfsVars].add(decl)
   if p.withinLoop > 0 and value == "":
     # fixes tests/run/tzeroarray:
     resetLoc(p, s.loc)
 
+proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode; didGenTemp: var bool) =
+  let s = vn.sym
+  fillBackendName(p.module, s)
+  fillLoc(s.loc, locGlobalVar, vn, OnHeap)
+  var decl: Rope = ""
+  let td = getTypeDesc(p.module, vn.sym.typ, dkVar)
+  genGlobalVarDecl(p, vn, td, "", decl)
+  decl.add " " & $s.loc.snippet
+  genCppVarForCtor(p, value, decl, didGenTemp)
+  if didGenTemp:  return # generated in the caller
+  p.module.s[cfsVars].add decl
+
 proc assignParam(p: BProc, s: PSym, retType: PType) =
-  assert(s.loc.r != "")
+  assert(s.loc.snippet != "")
   scopeMangledParam(p, s)
 
 proc fillProcLoc(m: BModule; n: PNode) =
@@ -744,6 +757,7 @@ proc intLiteral(i: BiggestInt; result: var Rope)
 proc genLiteral(p: BProc, n: PNode; result: var Rope)
 proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope; argsCounter: var int)
 proc raiseExit(p: BProc)
+proc raiseExitCleanup(p: BProc, destroy: string)
 
 proc initLocExpr(p: BProc, e: PNode, flags: TLocFlags = {}): TLoc =
   result = initLoc(locNone, e, OnUnknown, flags)
@@ -770,15 +784,11 @@ $1define nimfr_(proc, file) \
   TFrame FR_; \
   FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; #nimFrame(&FR_);
 
-  $1define nimfrs_(proc, file, slots, length) \
-    struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename;NI len;VarSlot s[slots];} FR_; \
-    FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = length; #nimFrame((TFrame*)&FR_);
+$1define nimln_(n) \
+  FR_.line = n;
 
-  $1define nimln_(n) \
-    FR_.line = n;
-
-  $1define nimlf_(n, file) \
-    FR_.line = n; FR_.filename = file;
+$1define nimlf_(n, file) \
+  FR_.line = n; FR_.filename = file;
 
 """
   if p.module.s[cfsFrameDefines].len == 0:
@@ -842,7 +852,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
       p.options.excl optStackTrace
       p.flags.incl nimErrorFlagDisabled
       var dest: TLoc = initLoc(locTemp, lib.path, OnStack)
-      dest.r = getTempName(m)
+      dest.snippet = getTempName(m)
       appcg(m, m.s[cfsDynLibInit],"$1 $2;$n",
            [getTypeDesc(m, lib.path.typ, dkVar), rdLoc(dest)])
       expr(p, lib.path, dest)
@@ -860,7 +870,7 @@ proc mangleDynLibProc(sym: PSym): Rope =
   # we have to build this as a single rope in order not to trip the
   # optimization in genInfixCall, see test tests/cpp/t8241.nim
   if sfCompilerProc in sym.flags:
-    # NOTE: sym.loc.r is the external name!
+    # NOTE: sym.loc.snippet is the external name!
     result = rope(sym.name.s)
   else:
     result = rope(strutils.`%`("Dl_$1_", $sym.id))
@@ -868,10 +878,10 @@ proc mangleDynLibProc(sym: PSym): Rope =
 proc symInDynamicLib(m: BModule, sym: PSym) =
   var lib = sym.annex
   let isCall = isGetProcAddr(lib)
-  var extname = sym.loc.r
+  var extname = sym.loc.snippet
   if not isCall: loadDynamicLib(m, lib)
   var tmp = mangleDynLibProc(sym)
-  sym.loc.r = tmp             # from now on we only need the internal name
+  sym.loc.snippet = tmp             # from now on we only need the internal name
   sym.typ.sym = nil           # generate a new name
   inc(m.labels, 2)
   if isCall:
@@ -898,24 +908,24 @@ proc symInDynamicLib(m: BModule, sym: PSym) =
     appcg(m, m.s[cfsDynLibInit],
         "\t$1 = ($2) #nimGetProcAddr($3, $4);$n",
         [tmp, getTypeDesc(m, sym.typ, dkVar), lib.name, makeCString($extname)])
-  m.s[cfsVars].addf("$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t, dkVar)])
+  m.s[cfsVars].addf("$2 $1;$n", [sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar)])
 
 proc varInDynamicLib(m: BModule, sym: PSym) =
   var lib = sym.annex
-  var extname = sym.loc.r
+  var extname = sym.loc.snippet
   loadDynamicLib(m, lib)
   incl(sym.loc.flags, lfIndirect)
   var tmp = mangleDynLibProc(sym)
-  sym.loc.r = tmp             # from now on we only need the internal name
+  sym.loc.snippet = tmp             # from now on we only need the internal name
   inc(m.labels, 2)
   appcg(m, m.s[cfsDynLibInit],
       "$1 = ($2*) #nimGetProcAddr($3, $4);$n",
       [tmp, getTypeDesc(m, sym.typ, dkVar), lib.name, makeCString($extname)])
   m.s[cfsVars].addf("$2* $1;$n",
-      [sym.loc.r, getTypeDesc(m, sym.loc.t, dkVar)])
+      [sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar)])
 
 proc symInDynamicLibPartial(m: BModule, sym: PSym) =
-  sym.loc.r = mangleDynLibProc(sym)
+  sym.loc.snippet = mangleDynLibProc(sym)
   sym.typ.sym = nil           # generate a new name
 
 proc cgsymImpl(m: BModule; sym: PSym) {.inline.} =
@@ -938,7 +948,7 @@ proc cgsymValue(m: BModule, name: string): Rope =
     cgsymImpl m, sym
   else:
     rawMessage(m.config, errGenerated, "system module needs: " & name)
-  result = sym.loc.r
+  result = sym.loc.snippet
   if m.hcrOn and sym != nil and sym.kind in {skProc..skIterator}:
     result.addActualSuffixForHCR(m.module, sym)
 
@@ -1065,7 +1075,11 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
       if result != Unknown: return result
   of nkAsgn, nkFastAsgn, nkSinkAsgn:
     if n[0].kind == nkSym and n[0].sym.kind == skResult:
-      if not containsResult(n[1]): result = InitSkippable
+      if not containsResult(n[1]):
+        if allPathsAsgnResult(p, n[1]) == InitRequired:
+          result = InitRequired
+        else:
+          result = InitSkippable
       else: result = InitRequired
     elif containsResult(n):
       result = InitRequired
@@ -1143,6 +1157,10 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
         allPathsInBranch(n[i])
   of nkRaiseStmt:
     result = InitRequired
+  of nkChckRangeF, nkChckRange64, nkChckRange:
+    # TODO: more checks might need to be covered like overflow, indexDefect etc.
+    # bug #22852
+    result = InitRequired
   else:
     for i in 0..<n.safeLen:
       allPathsInBranch(n[i])
@@ -1197,7 +1215,7 @@ proc genProcAux*(m: BModule, prc: PSym) =
       else:
         # declare the result symbol:
         assignLocalVar(p, resNode)
-        assert(res.loc.r != "")
+        assert(res.loc.snippet != "")
         if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and
             allPathsAsgnResult(p, procBody) == InitSkippable:
           # In an ideal world the codegen could rely on injectdestructors doing its job properly
@@ -1209,7 +1227,7 @@ proc genProcAux*(m: BModule, prc: PSym) =
     elif sfConstructor in prc.flags:
       resNode.sym.loc.flags.incl lfIndirect
       fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap)
-      prc.loc.r = getTypeDesc(m, resNode.sym.loc.t, dkVar)
+      prc.loc.snippet = getTypeDesc(m, resNode.sym.loc.t, dkVar)
     else:
       fillResult(p.config, resNode, prc.typ)
       assignParam(p, res, prc.typ.returnType)
@@ -1274,7 +1292,7 @@ proc genProcAux*(m: BModule, prc: PSym) =
   m.s[cfsProcs].add(generatedProc)
   if isReloadable(m, prc):
     m.s[cfsDynLibInit].addf("\t$1 = ($3) hcrRegisterProc($4, \"$1\", (void*)$2);$n",
-         [prc.loc.r, prc.loc.r & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
+         [prc.loc.snippet, prc.loc.snippet & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
 
 proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} =
   result = (sfCompileToCpp in m.module.flags and
@@ -1334,7 +1352,7 @@ proc genProcNoForward(m: BModule, prc: PSym) =
       # the hcr dynlib (also put it in the DynLibInit section - right after it gets loaded)
       if isReloadable(q, prc):
         q.s[cfsDynLibInit].addf("\t$1 = ($2) hcrRegisterProc($3, \"$1\", (void*)$1);$n",
-            [prc.loc.r, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)])
+            [prc.loc.snippet, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)])
     else:
       symInDynamicLibPartial(m, prc)
   elif prc.typ.callConv == ccInline:
@@ -1351,8 +1369,8 @@ proc genProcNoForward(m: BModule, prc: PSym) =
       #elif {sfExportc, sfImportc} * prc.flags == {}:
       #  # reset name to restore consistency in case of hashing collisions:
       #  echo "resetting ", prc.id, " by ", m.module.name.s
-      #  prc.loc.r = nil
-      #  prc.loc.r = mangleName(m, prc)
+      #  prc.loc.snippet = nil
+      #  prc.loc.snippet = mangleName(m, prc)
       genProcPrototype(m, prc)
       genProcAux(m, prc)
   elif sfImportc notin prc.flags:
@@ -1364,7 +1382,7 @@ proc genProcNoForward(m: BModule, prc: PSym) =
     if isReloadable(m, prc) and prc.id notin m.declaredProtos and
       q != nil and q.module.id != m.module.id:
       m.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n",
-           [prc.loc.r, getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
+           [prc.loc.snippet, getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
     genProcPrototype(m, prc)
     if q != nil and not containsOrIncl(q.declaredThings, prc.id):
       # make sure there is a "prototype" in the external module
@@ -1417,7 +1435,7 @@ proc genVarPrototype(m: BModule, n: PNode) =
     return
   if sym.owner.id != m.module.id:
     # else we already have the symbol generated!
-    assert(sym.loc.r != "")
+    assert(sym.loc.snippet != "")
     incl(m.declaredThings, sym.id)
     if sfThread in sym.flags:
       declareThreadVar(m, sym, true)
@@ -1431,9 +1449,9 @@ proc genVarPrototype(m: BModule, n: PNode) =
       if sfRegister in sym.flags: m.s[cfsVars].add(" register")
       if sfVolatile in sym.flags: m.s[cfsVars].add(" volatile")
       if sfNoalias in sym.flags: m.s[cfsVars].add(" NIM_NOALIAS")
-      m.s[cfsVars].addf(" $1;$n", [sym.loc.r])
+      m.s[cfsVars].addf(" $1;$n", [sym.loc.snippet])
       if m.hcrOn: m.initProc.procSec(cpsLocals).addf(
-        "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
+        "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.snippet,
         getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(m, sym)])
 
 proc addNimDefines(result: var Rope; conf: ConfigRef) {.inline.} =
@@ -1466,15 +1484,13 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
 
 proc getSomeNameForModule(conf: ConfigRef, filename: AbsoluteFile): Rope =
   ## Returns a mangled module name.
-  result = ""
-  result.add mangleModuleName(conf, filename).mangle
+  result = mangleModuleName(conf, filename).mangle
 
 proc getSomeNameForModule(m: BModule): Rope =
   ## Returns a mangled module name.
   assert m.module.kind == skModule
   assert m.module.owner.kind == skPackage
-  result = ""
-  result.add mangleModuleName(m.g.config, m.filename).mangle
+  result = mangleModuleName(m.g.config, m.filename).mangle
 
 proc getSomeInitName(m: BModule, suffix: string): Rope =
   if not m.hcrOn:
@@ -1810,7 +1826,7 @@ proc genDatInitCode(m: BModule) =
 
   # we don't want to break into such init code - could happen if a line
   # directive from a function written by the user spills after itself
-  genCLineDir(prc, "generated_not_to_break_here", 999999, m.config)
+  genCLineDir(prc, InvalidFileIdx, 999999, m.config)
 
   for i in cfsTypeInit1..cfsDynLibInit:
     if m.s[i].len != 0:
@@ -1832,11 +1848,11 @@ proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): R
 
   var extname = prefix & sym
   var tmp = mangleDynLibProc(prc)
-  prc.loc.r = tmp
+  prc.loc.snippet = tmp
   prc.typ.sym = nil
 
   if not containsOrIncl(m.declaredThings, prc.id):
-    m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.r, getTypeDesc(m, prc.loc.t, dkVar)])
+    m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.snippet, getTypeDesc(m, prc.loc.t, dkVar)])
 
   result = "\t$1 = ($2) $3($4, $5);$n" %
       [tmp, getTypeDesc(m, prc.typ, dkVar), getProcFunc.rope, handle.rope, makeCString(prefix & sym)]
@@ -1851,7 +1867,7 @@ proc genInitCode(m: BModule) =
     [rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), initname]
   # we don't want to break into such init code - could happen if a line
   # directive from a function written by the user spills after itself
-  genCLineDir(prc, "generated_not_to_break_here", 999999, m.config)
+  genCLineDir(prc, InvalidFileIdx, 999999, m.config)
   if m.typeNodes > 0:
     if m.hcrOn:
       appcg(m, m.s[cfsTypeInit1], "\t#TNimNode* $1;$N", [m.typeNodesName])
@@ -1921,9 +1937,6 @@ proc genInitCode(m: BModule) =
 
     if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
       prc.add(deinitFrame(m.initProc))
-  elif m.config.exc == excGoto:
-    if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil:
-      m.appcg(prc, "\t#nimTestErrorFlag();$n", [])
 
   prc.addf("}$N", [])
 
@@ -1966,6 +1979,40 @@ proc genInitCode(m: BModule) =
 
   registerModuleToMain(m.g, m)
 
+proc postprocessCode(conf: ConfigRef, r: var Rope) =
+  # find the first directive
+  var f = r.find(postprocessDirStart)
+  if f == -1:
+    return
+
+  var
+    nimlnDirLastF = ""
+
+  var res: Rope = r.substr(0, f - 1)
+  while f != -1:
+    var
+      e = r.find(postprocessDirEnd, f + 1)
+      dir = r.substr(f + 1, e - 1).split(postprocessDirSep)
+    case dir[0]
+    of "nimln":
+      if dir[2] == nimlnDirLastF:
+        res.add("nimln_(" & dir[1] & ");")
+      else:
+        res.add("nimlf_(" & dir[1] & ", " & quotedFilename(conf, dir[2].parseInt.FileIndex) & ");")
+        nimlnDirLastF = dir[2]
+    else:
+      raiseAssert "unexpected postprocess directive"
+
+    # find the next directive
+    f = r.find(postprocessDirStart, e + 1)
+    # copy the code until the next directive
+    if f != -1:
+      res.add(r.substr(e + 1, f - 1))
+    else:
+      res.add(r.substr(e + 1))
+
+  r = res
+
 proc genModule(m: BModule, cfile: Cfile): Rope =
   var moduleIsEmpty = true
 
@@ -1994,9 +2041,17 @@ proc genModule(m: BModule, cfile: Cfile): Rope =
   if m.config.cppCustomNamespace.len > 0:
     closeNamespaceNim(result)
 
+  if optLineDir in m.config.options:
+    var srcFileDefs = ""
+    for fi in 0..m.config.m.fileInfos.high:
+      srcFileDefs.add("#define FX_" & $fi & " " & makeSingleLineCString(toFullPath(m.config, fi.FileIndex)) & "\n")
+    result = srcFileDefs & result
+
   if moduleIsEmpty:
     result = ""
 
+  postprocessCode(m.config, result)
+
 proc initProcOptions(m: BModule): TOptions =
   let opts = m.config.options
   if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts
@@ -2202,7 +2257,7 @@ proc generateLibraryDestroyGlobals(graph: ModuleGraph; m: BModule; body: PNode;
   result.typ = newProcType(m.module.info, m.idgen, m.module.owner)
   result.typ.callConv = ccCDecl
   incl result.flags, sfExportc
-  result.loc.r = "NimDestroyGlobals"
+  result.loc.snippet = "NimDestroyGlobals"
   if isDynlib:
     incl(result.loc.flags, lfExportLib)
 
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index 9f71a8292..5368e9dc7 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -88,7 +88,7 @@ type
     options*: TOptions        # options that should be used for code
                               # generation; this is the same as prc.options
                               # unless prc == nil
-    optionsStack*: seq[TOptions]
+    optionsStack*: seq[(TOptions, TNoteKinds)]
     module*: BModule          # used to prevent excessive parameter passing
     withinLoop*: int          # > 0 if we are within a loop
     splitDecls*: int          # > 0 if we are in some context for C++ that
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index b132c6049..ca97d0494 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -133,7 +133,7 @@ proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
   if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
   disp.ast = copyTree(s.ast)
   disp.ast[bodyPos] = newNodeI(nkEmpty, s.info)
-  disp.loc.r = ""
+  disp.loc.snippet = ""
   if s.typ.returnType != nil:
     if disp.ast.len > resultPos:
       disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, idgen)
diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim
index c1f525488..8bdd04ca7 100644
--- a/compiler/closureiters.nim
+++ b/compiler/closureiters.nim
@@ -36,13 +36,6 @@
 #  else:
 #    return
 
-# The transformation should play well with lambdalifting, however depending
-# on situation, it can be called either before or after lambdalifting
-# transformation. As such we behave slightly differently, when accessing
-# iterator state, or using temp variables. If lambdalifting did not happen,
-# we just create local variables, so that they will be lifted further on.
-# Otherwise, we utilize existing env, created by lambdalifting.
-
 # Lambdalifting treats :state variable specially, it should always end up
 # as the first field in env. Currently C codegen depends on this behavior.
 
@@ -151,7 +144,6 @@ type
   Ctx = object
     g: ModuleGraph
     fn: PSym
-    stateVarSym: PSym # :state variable. nil if env already introduced by lambdalifting
     tmpResultSym: PSym # Used when we return, but finally has to interfere
     unrollFinallySym: PSym # Indicates that we're unrolling finally states (either exception happened or premature return)
     curExcSym: PSym # Current exception
@@ -168,16 +160,23 @@ type
     nearestFinally: int # Index of the nearest finally block. For try/except it
                     # is their finally. For finally it is parent finally. Otherwise -1
     idgen: IdGenerator
+    varStates: Table[ItemId, int] # Used to detect if local variable belongs to multiple states
+    stateVarSym: PSym # :state variable. nil if env already introduced by lambdalifting
+      # remove if -d:nimOptIters is default, treating it as always nil
+    nimOptItersEnabled: bool # tracks if -d:nimOptIters is enabled
+      # should be default when issues are fixed, see #24094
 
 const
   nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
             nkCommentStmt, nkMixinStmt, nkBindStmt} + procDefs
   emptyStateLabel = -1
+  localNotSeen = -1
+  localRequiresLifting = -2
 
 proc newStateAccess(ctx: var Ctx): PNode =
   if ctx.stateVarSym.isNil:
     result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)),
-        getStateField(ctx.g, ctx.fn), ctx.fn.info)
+      getStateField(ctx.g, ctx.fn), ctx.fn.info)
   else:
     result = newSymNode(ctx.stateVarSym)
 
@@ -195,7 +194,7 @@ proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym =
   result = newSym(skVar, getIdent(ctx.g.cache, name), ctx.idgen, ctx.fn, ctx.fn.info)
   result.typ = typ
   result.flags.incl sfNoInit
-  assert(not typ.isNil)
+  assert(not typ.isNil, "Env var needs a type")
 
   if not ctx.stateVarSym.isNil:
     # We haven't gone through labmda lifting yet, so just create a local var,
@@ -214,6 +213,9 @@ proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode =
   else:
     result = newSymNode(s)
 
+proc newTempVarAccess(ctx: Ctx, s: PSym): PNode =
+  result = newSymNode(s, ctx.fn.info)
+
 proc newTmpResultAccess(ctx: var Ctx): PNode =
   if ctx.tmpResultSym.isNil:
     ctx.tmpResultSym = ctx.newEnvVar(":tmpResult", ctx.fn.typ.returnType)
@@ -255,9 +257,26 @@ proc addGotoOut(n: PNode, gotoOut: PNode): PNode =
   if result.len == 0 or result[^1].kind != nkGotoState:
     result.add(gotoOut)
 
-proc newTempVar(ctx: var Ctx, typ: PType): PSym =
-  result = ctx.newEnvVar(":tmpSlLower" & $ctx.tempVarId, typ)
+proc newTempVarDef(ctx: Ctx, s: PSym, initialValue: PNode): PNode =
+  var v = initialValue
+  if v == nil:
+    v = ctx.g.emptyNode
+  newTree(nkVarSection, newTree(nkIdentDefs, newSymNode(s), ctx.g.emptyNode, v))
+
+proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode
+
+proc newTempVar(ctx: var Ctx, typ: PType, parent: PNode, initialValue: PNode = nil): PSym =
+  if ctx.nimOptItersEnabled:
+    result = newSym(skVar, getIdent(ctx.g.cache, ":tmpSlLower" & $ctx.tempVarId), ctx.idgen, ctx.fn, ctx.fn.info)
+  else:
+    result = ctx.newEnvVar(":tmpSlLower" & $ctx.tempVarId, typ)
   inc ctx.tempVarId
+  result.typ = typ
+  assert(not typ.isNil, "Temp var needs a type")
+  if ctx.nimOptItersEnabled:
+    parent.add(ctx.newTempVarDef(result, initialValue))
+  elif initialValue != nil:
+    parent.add(ctx.newEnvVarAsgn(result, initialValue))
 
 proc hasYields(n: PNode): bool =
   # TODO: This is very inefficient. It traverses the node, looking for nkYieldStmt.
@@ -429,8 +448,15 @@ proc exprToStmtList(n: PNode): tuple[s, res: PNode] =
 
   result.res = n
 
+proc newTempVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode =
+  if isEmptyType(v.typ):
+    result = v
+  else:
+    result = newTree(nkFastAsgn, ctx.newTempVarAccess(s), v)
+    result.info = v.info
 
 proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode =
+  # unused with -d:nimOptIters
   if isEmptyType(v.typ):
     result = v
   else:
@@ -438,10 +464,13 @@ proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode =
     result.info = v.info
 
 proc addExprAssgn(ctx: Ctx, output, input: PNode, sym: PSym) =
+  var input = input
   if input.kind == nkStmtListExpr:
     let (st, res) = exprToStmtList(input)
     output.add(st)
-    output.add(ctx.newEnvVarAsgn(sym, res))
+    input = res
+  if ctx.nimOptItersEnabled:
+    output.add(ctx.newTempVarAsgn(sym, input))
   else:
     output.add(ctx.newEnvVarAsgn(sym, input))
 
@@ -457,6 +486,12 @@ proc boolLit(g: ModuleGraph; info: TLineInfo; value: bool): PNode =
   result = newIntLit(g, info, ord value)
   result.typ = getSysType(g, info, tyBool)
 
+proc captureVar(c: var Ctx, s: PSym) =
+  if c.varStates.getOrDefault(s.itemId) != localRequiresLifting:
+    c.varStates[s.itemId] = localRequiresLifting # Mark this variable for lifting
+    let e = getEnvParam(c.fn)
+    discard addField(e.typ.elementType, s, c.g.cache, c.idgen)
+
 proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
   result = n
   case n.kind
@@ -513,9 +548,9 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
       var tmp: PSym = nil
       let isExpr = not isEmptyType(n.typ)
       if isExpr:
-        tmp = ctx.newTempVar(n.typ)
         result = newNodeI(nkStmtListExpr, n.info)
         result.typ = n.typ
+        tmp = ctx.newTempVar(n.typ, result)
       else:
         result = newNodeI(nkStmtList, n.info)
 
@@ -566,7 +601,11 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
         else:
           internalError(ctx.g.config, "lowerStmtListExpr(nkIf): " & $branch.kind)
 
-      if isExpr: result.add(ctx.newEnvVarAccess(tmp))
+      if isExpr:
+        if ctx.nimOptItersEnabled:
+          result.add(ctx.newTempVarAccess(tmp))
+        else:
+          result.add(ctx.newEnvVarAccess(tmp))
 
   of nkTryStmt, nkHiddenTryStmt:
     var ns = false
@@ -580,7 +619,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
       if isExpr:
         result = newNodeI(nkStmtListExpr, n.info)
         result.typ = n.typ
-        let tmp = ctx.newTempVar(n.typ)
+        let tmp = ctx.newTempVar(n.typ, result)
 
         n[0] = ctx.convertExprBodyToAsgn(n[0], tmp)
         for i in 1..<n.len:
@@ -596,7 +635,10 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
           else:
             internalError(ctx.g.config, "lowerStmtListExpr(nkTryStmt): " & $branch.kind)
         result.add(n)
-        result.add(ctx.newEnvVarAccess(tmp))
+        if ctx.nimOptItersEnabled:
+          result.add(ctx.newTempVarAccess(tmp))
+        else:
+          result.add(ctx.newEnvVarAccess(tmp))
 
   of nkCaseStmt:
     var ns = false
@@ -609,9 +651,9 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
       let isExpr = not isEmptyType(n.typ)
 
       if isExpr:
-        let tmp = ctx.newTempVar(n.typ)
         result = newNodeI(nkStmtListExpr, n.info)
         result.typ = n.typ
+        let tmp = ctx.newTempVar(n.typ, result)
 
         if n[0].kind == nkStmtListExpr:
           let (st, ex) = exprToStmtList(n[0])
@@ -628,7 +670,10 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
           else:
             internalError(ctx.g.config, "lowerStmtListExpr(nkCaseStmt): " & $branch.kind)
         result.add(n)
-        result.add(ctx.newEnvVarAccess(tmp))
+        if ctx.nimOptItersEnabled:
+          result.add(ctx.newTempVarAccess(tmp))
+        else:
+          result.add(ctx.newEnvVarAccess(tmp))
       elif n[0].kind == nkStmtListExpr:
         result = newNodeI(nkStmtList, n.info)
         let (st, ex) = exprToStmtList(n[0])
@@ -658,10 +703,14 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
           result.add(st)
           cond = ex
 
-        let tmp = ctx.newTempVar(cond.typ)
-        result.add(ctx.newEnvVarAsgn(tmp, cond))
+        let tmp = ctx.newTempVar(cond.typ, result, cond)
+        # result.add(ctx.newTempVarAsgn(tmp, cond))
 
-        var check = ctx.newEnvVarAccess(tmp)
+        var check: PNode
+        if ctx.nimOptItersEnabled:
+          check = ctx.newTempVarAccess(tmp)
+        else:
+          check = ctx.newEnvVarAccess(tmp)
         if n[0].sym.magic == mOr:
           check = ctx.g.newNotCall(check)
 
@@ -671,12 +720,18 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
           let (st, ex) = exprToStmtList(cond)
           ifBody.add(st)
           cond = ex
-        ifBody.add(ctx.newEnvVarAsgn(tmp, cond))
+        if ctx.nimOptItersEnabled:
+          ifBody.add(ctx.newTempVarAsgn(tmp, cond))
+        else:
+          ifBody.add(ctx.newEnvVarAsgn(tmp, cond))
 
         let ifBranch = newTree(nkElifBranch, check, ifBody)
         let ifNode = newTree(nkIfStmt, ifBranch)
         result.add(ifNode)
-        result.add(ctx.newEnvVarAccess(tmp))
+        if ctx.nimOptItersEnabled:
+          result.add(ctx.newTempVarAccess(tmp))
+        else:
+          result.add(ctx.newEnvVarAccess(tmp))
       else:
         for i in 0..<n.len:
           if n[i].kind == nkStmtListExpr:
@@ -685,9 +740,12 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
             n[i] = ex
 
           if n[i].kind in nkCallKinds: # XXX: This should better be some sort of side effect tracking
-            let tmp = ctx.newTempVar(n[i].typ)
-            result.add(ctx.newEnvVarAsgn(tmp, n[i]))
-            n[i] = ctx.newEnvVarAccess(tmp)
+            let tmp = ctx.newTempVar(n[i].typ, result, n[i])
+            # result.add(ctx.newTempVarAsgn(tmp, n[i]))
+            if ctx.nimOptItersEnabled:
+              n[i] = ctx.newTempVarAccess(tmp)
+            else:
+              n[i] = ctx.newEnvVarAccess(tmp)
 
         result.add(n)
 
@@ -703,6 +761,12 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
         let (st, ex) = exprToStmtList(c[^1])
         result.add(st)
         c[^1] = ex
+      for i in 0 .. c.len - 3:
+        if c[i].kind == nkSym:
+          let s = c[i].sym
+          if sfForceLift in s.flags:
+            ctx.captureVar(s)
+
       result.add(varSect)
 
   of nkDiscardStmt, nkReturnStmt, nkRaiseStmt:
@@ -1433,15 +1497,77 @@ proc preprocess(c: var PreprocessContext; n: PNode): PNode =
     for i in 0 ..< n.len:
       result[i] = preprocess(c, n[i])
 
+proc detectCapturedVars(c: var Ctx, n: PNode, stateIdx: int) =
+  case n.kind
+  of nkSym:
+    let s = n.sym
+    if s.kind in {skResult, skVar, skLet, skForVar, skTemp} and sfGlobal notin s.flags and s.owner == c.fn:
+      let vs = c.varStates.getOrDefault(s.itemId, localNotSeen)
+      if vs == localNotSeen: # First seing this variable
+        c.varStates[s.itemId] = stateIdx
+      elif vs == localRequiresLifting:
+        discard # Sym already marked
+      elif vs != stateIdx:
+        c.captureVar(s)
+  of nkReturnStmt:
+    if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}:
+      # we have a `result = result` expression produced by the closure
+      # transform, let's not touch the LHS in order to make the lifting pass
+      # correct when `result` is lifted
+      detectCapturedVars(c, n[0][1], stateIdx)
+    else:
+      detectCapturedVars(c, n[0], stateIdx)
+  else:
+    for i in 0 ..< n.safeLen:
+      detectCapturedVars(c, n[i], stateIdx)
+
+proc detectCapturedVars(c: var Ctx) =
+  for i, s in c.states:
+    detectCapturedVars(c, s.body, i)
+
+proc liftLocals(c: var Ctx, n: PNode): PNode =
+  result = n
+  case n.kind
+  of nkSym:
+    let s = n.sym
+    if c.varStates.getOrDefault(s.itemId) == localRequiresLifting:
+      # lift
+      let e = getEnvParam(c.fn)
+      let field = getFieldFromObj(e.typ.elementType, s)
+      assert(field != nil)
+      result = rawIndirectAccess(newSymNode(e), field, n.info)
+    # elif c.varStates.getOrDefault(s.itemId, localNotSeen) != localNotSeen:
+    #   echo "Not lifting ", s.name.s
+
+  of nkReturnStmt:
+    if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}:
+      # we have a `result = result` expression produced by the closure
+      # transform, let's not touch the LHS in order to make the lifting pass
+      # correct when `result` is lifted
+      n[0][1] = liftLocals(c, n[0][1])
+    else:
+      n[0] = liftLocals(c, n[0])
+  else:
+    for i in 0 ..< n.safeLen:
+      n[i] = liftLocals(c, n[i])
+
 proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n: PNode): PNode =
-  var ctx = Ctx(g: g, fn: fn, idgen: idgen)
+  var ctx = Ctx(g: g, fn: fn, idgen: idgen,
+    # should be default when issues are fixed, see #24094:
+    nimOptItersEnabled: isDefined(g.config, "nimOptIters"))
 
   if getEnvParam(fn).isNil:
-    # Lambda lifting was not done yet. Use temporary :state sym, which will
-    # be handled specially by lambda lifting. Local temp vars (if needed)
-    # should follow the same logic.
-    ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), idgen, fn, fn.info)
-    ctx.stateVarSym.typ = g.createClosureIterStateType(fn, idgen)
+    if ctx.nimOptItersEnabled:
+      # The transformation should always happen after at least partial lambdalifting
+      # is performed, so that the closure iter environment is always created upfront.
+      doAssert(false, "Env param not created before iter transformation")
+    else:
+      # Lambda lifting was not done yet. Use temporary :state sym, which will
+      # be handled specially by lambda lifting. Local temp vars (if needed)
+      # should follow the same logic.
+      ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), idgen, fn, fn.info)
+      ctx.stateVarSym.typ = g.createClosureIterStateType(fn, idgen)
+
   ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), idgen, fn, fn.info)
   var pc = PreprocessContext(finallys: @[], config: g.config, idgen: idgen)
   var n = preprocess(pc, n.toStmtList)
@@ -1466,6 +1592,11 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
   let caseDispatcher = newTreeI(nkCaseStmt, n.info,
       ctx.newStateAccess())
 
+  if ctx.nimOptItersEnabled:
+    # Lamdalifting will not touch our locals, it is our responsibility to lift those that
+    # need it.
+    detectCapturedVars(ctx)
+
   for s in ctx.states:
     let body = ctx.transformStateAssignments(s.body)
     caseDispatcher.add newTreeI(nkOfBranch, body.info, g.newIntLit(body.info, s.label), body)
@@ -1473,6 +1604,8 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
   caseDispatcher.add newTreeI(nkElse, n.info, newTreeI(nkReturnStmt, n.info, g.emptyNode))
 
   result = wrapIntoStateLoop(ctx, caseDispatcher)
+  if ctx.nimOptItersEnabled:
+    result = liftLocals(ctx, result)
 
   when false:
     echo "TRANSFORM TO STATES: "
@@ -1481,3 +1614,5 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
     echo "exception table:"
     for i, e in ctx.exceptionTable:
       echo i, " -> ", e
+
+    echo "ENV: ", renderTree(getEnvParam(fn).typ.elementType.n)
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 176b73044..cbf915ca6 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -24,7 +24,7 @@ bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
 bootSwitch(usedGoGC, defined(gogc), "--gc:go")
 bootSwitch(usedNoGC, defined(nogc), "--gc:none")
 
-import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs]
+import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs, enumutils]
 import
   msgs, options, nversion, condsyms, extccomp, platform,
   wordrecg, nimblecmd, lineinfos, pathutils
@@ -248,6 +248,7 @@ const
   errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
   errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found"
   errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
+  errInvalidFeatureButXFound = Feature.toSeq.map(proc(val:Feature): string = "'$1'" % $val).join(", ") & " expected, but '$1' found"
 
 template warningOptionNoop(switch: string) =
   warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
@@ -303,6 +304,12 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
     else:
       result = false
       localError(conf, info, errInvalidExceptionSystem % arg)
+  of "experimental":
+    try:
+      result = conf.features.contains parseEnum[Feature](arg)
+    except ValueError:
+      result = false
+      localError(conf, info, errInvalidFeatureButXFound % arg)
   else:
     result = false
     invalidCmdLineOption(conf, passCmd1, switch, info)
@@ -462,7 +469,6 @@ proc handleCmdInput*(conf: ConfigRef) =
 proc parseCommand*(command: string): Command =
   case command.normalize
   of "c", "cc", "compile", "compiletoc": cmdCompileToC
-  of "nir": cmdCompileToNir
   of "cpp", "compiletocpp": cmdCompileToCpp
   of "objc", "compiletooc": cmdCompileToOC
   of "js", "compiletojs": cmdCompileToJS
@@ -500,7 +506,6 @@ proc setCmd*(conf: ConfigRef, cmd: Command) =
   of cmdCompileToCpp: conf.backend = backendCpp
   of cmdCompileToOC: conf.backend = backendObjc
   of cmdCompileToJS: conf.backend = backendJs
-  of cmdCompileToNir: conf.backend = backendNir
   else: discard
 
 proc setCommandEarly*(conf: ConfigRef, command: string) =
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index 085576c6b..5043fc5d4 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -166,3 +166,6 @@ proc initDefines*(symbols: StringTableRef) =
   defineSymbol("nimHasWarnStdPrefix")
 
   defineSymbol("nimHasVtables")
+  defineSymbol("nimHasGenericsOpenSym2")
+  defineSymbol("nimHasGenericsOpenSym3")
+  defineSymbol("nimHasJsNoLambdaLifting")
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index 8ee527952..5534d07e7 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -46,10 +46,10 @@ type
     case isTryBlock: bool
     of false:
       label: PSym
-      breakFixups: seq[(TPosition, seq[PNode])] #Contains the gotos for the breaks along with their pending finales
+      breakFixups: seq[(TPosition, seq[PNode])] # Contains the gotos for the breaks along with their pending finales
     of true:
       finale: PNode
-      raiseFixups: seq[TPosition] #Contains the gotos for the raises
+      raiseFixups: seq[TPosition] # Contains the gotos for the raises
 
   Con = object
     code: ControlFlowGraph
@@ -181,14 +181,6 @@ proc genIf(c: var Con, n: PNode) =
     goto Lend3
   L3:
     D
-    goto Lend3 # not eliminated to simplify the join generation
-  Lend3:
-    join F3
-  Lend2:
-    join F2
-  Lend:
-    join F1
-
   ]#
   var endings: seq[TPosition] = @[]
   let oldInteresting = c.interestingInstructions
@@ -213,7 +205,6 @@ proc genAndOr(c: var Con; n: PNode) =
   #   fork lab1
   #   asgn dest, b
   # lab1:
-  #   join F1
   c.gen(n[1])
   forkT:
     c.gen(n[2])
@@ -324,7 +315,7 @@ proc genRaise(c: var Con; n: PNode) =
       if c.blocks[i].isTryBlock:
         genBreakOrRaiseAux(c, i, n)
         return
-    assert false #Unreachable
+    assert false # Unreachable
   else:
     genNoReturn(c)
 
@@ -390,7 +381,6 @@ proc genCall(c: var Con; n: PNode) =
     # fork lab1
     # goto exceptionHandler (except or finally)
     # lab1:
-    # join F1
     forkT:
       for i in countdown(c.blocks.high, 0):
         if c.blocks[i].isTryBlock:
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index b4c4baa2b..8e5f5e4e7 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -831,7 +831,7 @@ proc getName(n: PNode): string =
     result = "`"
     for i in 0..<n.len: result.add(getName(n[i]))
     result = "`"
-  of nkOpenSymChoice, nkClosedSymChoice:
+  of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
     result = getName(n[0])
   else:
     result = ""
@@ -849,7 +849,7 @@ proc getNameIdent(cache: IdentCache; n: PNode): PIdent =
     var r = ""
     for i in 0..<n.len: r.add(getNameIdent(cache, n[i]).s)
     result = getIdent(cache, r)
-  of nkOpenSymChoice, nkClosedSymChoice:
+  of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
     result = getNameIdent(cache, n[0])
   else:
     result = nil
@@ -863,7 +863,7 @@ proc getRstName(n: PNode): PRstNode =
   of nkAccQuoted:
     result = getRstName(n[0])
     for i in 1..<n.len: result.text.add(getRstName(n[i]).text)
-  of nkOpenSymChoice, nkClosedSymChoice:
+  of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
     result = getRstName(n[0])
   else:
     result = nil
@@ -1206,7 +1206,7 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false):
         var param = %{"name": %($genericParam)}
         if genericParam.sym.typ.len > 0:
           param["types"] = newJArray()
-        param["types"].add %($genericParam.sym.typ.elementType)
+          param["types"] = %($genericParam.sym.typ.elementType)
         result.json["signature"]["genericParams"].add param
   if optGenIndex in d.conf.globalOptions:
     genItem(d, n, nameNode, k, kForceExport)
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index bd0875213..77c136d63 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -33,6 +33,19 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
     let x = param
     if x.kind == nkArgList:
       for y in items(x): result.add(y)
+    elif nfDefaultRefsParam in x.flags:
+      # value of default param needs to be evaluated like template body
+      # if it contains other template params
+      var res: PNode
+      if isAtom(x):
+        res = newNodeI(nkPar, x.info)
+        evalTemplateAux(x, actual, c, res)
+        if res.len == 1: res = res[0]
+      else:
+        res = copyNode(x)
+        for i in 0..<x.safeLen:
+          evalTemplateAux(x[i], actual, c, res)
+      result.add res
     else:
       result.add copyTree(x)
 
@@ -56,6 +69,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
           #  internalAssert c.config, false
           idTablePut(c.mapping, s, x)
         if sfGenSym in s.flags:
+          # TODO: getIdent(c.ic, "`" & x.name.s & "`gensym" & $c.instID)
           result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $c.instID),
             if c.instLines: actual.info else: templ.info)
         else:
diff --git a/compiler/expanddefaults.nim b/compiler/expanddefaults.nim
index c121f5dea..c520d8849 100644
--- a/compiler/expanddefaults.nim
+++ b/compiler/expanddefaults.nim
@@ -125,7 +125,7 @@ proc expandDefault(t: PType; info: TLineInfo): PNode =
   of tyString:
     result = newZero(t, info, nkStrLit)
   of tyNone, tyEmpty, tyUntyped, tyTyped, tyTypeDesc,
-     tyNil, tyGenericInvocation, tyProxy, tyBuiltInTypeClass,
+     tyNil, tyGenericInvocation, tyError, tyBuiltInTypeClass,
      tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass,
      tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward:
     result = newZero(t, info, nkEmpty) # bug indicator
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 0ea458e3b..ce25da773 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -172,6 +172,22 @@ compiler vcc:
     cppXsupport: "",
     props: {hasCpp, hasAssume, hasDeclspec})
 
+# Nvidia CUDA NVCC Compiler
+compiler nvcc:
+  result = gcc()
+  result.name = "nvcc"
+  result.compilerExe = "nvcc"
+  result.cppCompiler = "nvcc"
+  result.compileTmpl = "-c -x cu -Xcompiler=\"$options\" $include -o $objfile $file"
+  result.linkTmpl = "$buildgui $builddll -o $exefile $objfiles -Xcompiler=\"$options\""
+
+# AMD HIPCC Compiler (rocm/cuda)
+compiler hipcc:
+  result = clang()
+  result.name = "hipcc"
+  result.compilerExe = "hipcc"
+  result.cppCompiler = "hipcc"
+
 compiler clangcl:
   result = vcc()
   result.name = "clang_cl"
@@ -285,7 +301,9 @@ const
     envcc(),
     icl(),
     icc(),
-    clangcl()]
+    clangcl(),
+    hipcc(),
+    nvcc()]
 
   hExt* = ".h"
 
@@ -319,7 +337,7 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
   var fullSuffix = suffix
   case conf.backend
   of backendCpp, backendJs, backendObjc: fullSuffix = "." & $conf.backend & suffix
-  of backendC, backendNir: discard
+  of backendC: discard
   of backendInvalid:
     # during parsing of cfg files; we don't know the backend yet, no point in
     # guessing wrong thing
@@ -982,10 +1000,11 @@ proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile =
   # works out of the box with `hashMainCompilationParams`.
   result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json")
 
-const cacheVersion = "D20210525T193831" # update when `BuildCache` spec changes
+const cacheVersion = "D20240927T193831" # update when `BuildCache` spec changes
 type BuildCache = object
   cacheVersion: string
   outputFile: string
+  outputLastModificationTime: string
   compile: seq[(string, string)]
   link: seq[string]
   linkcmd: string
@@ -1029,6 +1048,8 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) =
           bcache.depfiles.add (path, $secureHashFile(path))
 
     bcache.nimexe = hashNimExe()
+    if fileExists(bcache.outputFile):
+      bcache.outputLastModificationTime = $getLastModificationTime(bcache.outputFile)
   conf.jsonBuildFile = conf.jsonBuildInstructionsFile
   conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty)
 
@@ -1049,6 +1070,8 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: Absolute
     # xxx optimize by returning false if stdin input was the same
   for (file, hash) in bcache.depfiles:
     if $secureHashFile(file) != hash: return true
+  if bcache.outputLastModificationTime != $getLastModificationTime(bcache.outputFile):
+    return true
 
 proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
   var bcache: BuildCache = default(BuildCache)
@@ -1065,7 +1088,7 @@ proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
       "jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " %
       [outputCurrent, output, jsonFile.string])
   var cmds: TStringSeq = default(TStringSeq)
-  var prettyCmds: TStringSeq= default(TStringSeq)
+  var prettyCmds: TStringSeq = default(TStringSeq)
   let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
   for (name, cmd) in bcache.compile:
     cmds.add cmd
diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim
index c9f546c76..8e81633ef 100644
--- a/compiler/ic/ic.nim
+++ b/compiler/ic/ic.nim
@@ -16,7 +16,7 @@ from std/os import removeFile, isAbsolute
 
 import ../../dist/checksums/src/checksums/sha1
 
-import ".." / nir / nirlineinfos
+import iclineinfos
 
 when defined(nimPreviewSlimSystem):
   import std/[syncio, assertions, formatfloat]
@@ -59,8 +59,8 @@ type
     emittedTypeInfo*: seq[string]
     backendFlags*: set[ModuleBackendFlag]
 
-    syms*: seq[PackedSym]
-    types*: seq[PackedType]
+    syms*: OrderedTable[int32, PackedSym]
+    types*: OrderedTable[int32, PackedType]
     strings*: BiTable[string] # we could share these between modules.
     numbers*: BiTable[BiggestInt] # we also store floats in here so
                                   # that we can assure that every bit is kept
@@ -362,10 +362,10 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI
   result = PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item)
 
   if t.uniqueId.module == c.thisModule and not c.typeMarker.containsOrIncl(t.uniqueId.item):
-    if t.uniqueId.item >= m.types.len:
-      setLen m.types, t.uniqueId.item+1
+    #if t.uniqueId.item >= m.types.len:
+    #  setLen m.types, t.uniqueId.item+1
 
-    var p = PackedType(kind: t.kind, flags: t.flags, callConv: t.callConv,
+    var p = PackedType(id: t.uniqueId.item, kind: t.kind, flags: t.flags, callConv: t.callConv,
       size: t.size, align: t.align, nonUniqueId: t.itemId.item,
       paddingAtEnd: t.paddingAtEnd)
     storeNode(p, t, n)
@@ -396,12 +396,12 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
   result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item)
 
   if s.itemId.module == c.thisModule and not c.symMarker.containsOrIncl(s.itemId.item):
-    if s.itemId.item >= m.syms.len:
-      setLen m.syms, s.itemId.item+1
+    #if s.itemId.item >= m.syms.len:
+    #  setLen m.syms, s.itemId.item+1
 
     assert sfForward notin s.flags
 
-    var p = PackedSym(kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c, m), magic: s.magic,
+    var p = PackedSym(id: s.itemId.item, kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c, m), magic: s.magic,
       position: s.position, offset: s.offset, disamb: s.disamb, options: s.options,
       name: s.name.s.toLitId(m))
 
@@ -414,7 +414,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
       p.bitsize = s.bitsize
       p.alignment = s.alignment
 
-    p.externalName = toLitId(s.loc.r, m)
+    p.externalName = toLitId(s.loc.snippet, m)
     p.locFlags = s.loc.flags
     c.addMissing s.typ
     p.typ = s.typ.storeType(c, m)
@@ -613,6 +613,10 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef
     f.loadSection section
     f.loadSeq data
 
+  template loadTableSection(section, data) {.dirty.} =
+    f.loadSection section
+    f.loadOrderedTable data
+
   template loadTabSection(section, data) {.dirty.} =
     f.loadSection section
     f.load data
@@ -645,8 +649,8 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef
     loadTabSection topLevelSection, m.topLevel
 
     loadTabSection bodiesSection, m.bodies
-    loadSeqSection symsSection, m.syms
-    loadSeqSection typesSection, m.types
+    loadTableSection symsSection, m.syms
+    loadTableSection typesSection, m.types
 
     loadSeqSection typeInstCacheSection, m.typeInstCache
     loadSeqSection procInstCacheSection, m.procInstCache
@@ -691,6 +695,10 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac
     f.storeSection section
     f.store data
 
+  template storeTableSection(section, data) {.dirty.} =
+    f.storeSection section
+    f.storeOrderedTable data
+
   storeTabSection stringsSection, m.strings
 
   storeSeqSection checkSumsSection, m.includes
@@ -714,9 +722,9 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac
   storeTabSection topLevelSection, m.topLevel
 
   storeTabSection bodiesSection, m.bodies
-  storeSeqSection symsSection, m.syms
+  storeTableSection symsSection, m.syms
 
-  storeSeqSection typesSection, m.types
+  storeTableSection typesSection, m.types
 
   storeSeqSection typeInstCacheSection, m.typeInstCache
   storeSeqSection procInstCacheSection, m.procInstCache
@@ -767,8 +775,8 @@ type
     status*: ModuleStatus
     symsInit, typesInit, loadedButAliveSetChanged*: bool
     fromDisk*: PackedModule
-    syms: seq[PSym] # indexed by itemId
-    types: seq[PType]
+    syms: OrderedTable[int32, PSym] # indexed by itemId
+    types: OrderedTable[int32, PType]
     module*: PSym # the one true module symbol.
     iface, ifaceHidden: Table[PIdent, seq[PackedItemId]]
       # PackedItemId so that it works with reexported symbols too
@@ -829,7 +837,7 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
     result.ident = getIdent(c.cache, g[thisModule].fromDisk.strings[n.litId])
   of nkSym:
     result.sym = loadSym(c, g, thisModule, PackedItemId(module: LitId(0), item: tree[n].soperand))
-    if result.typ == nil and nfOpenSym notin result.flags:
+    if result.typ == nil:
       result.typ = result.sym.typ
   of externIntLit:
     result.intVal = g[thisModule].fromDisk.numbers[n.litId]
@@ -843,7 +851,7 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
     assert n2.kind == nkNone
     transitionNoneToSym(result)
     result.sym = loadSym(c, g, thisModule, PackedItemId(module: n1.litId, item: tree[n2].soperand))
-    if result.typ == nil and nfOpenSym notin result.flags:
+    if result.typ == nil:
       result.typ = result.sym.typ
   else:
     for n0 in sonsReadonly(tree, n):
@@ -937,7 +945,7 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
   result.owner = loadSym(c, g, si, s.owner)
   let externalName = g[si].fromDisk.strings[s.externalName]
   if externalName != "":
-    result.loc.r = rope externalName
+    result.loc.snippet = externalName
   result.loc.flags = s.locFlags
   result.instantiatedFrom = loadSym(c, g, si, s.instantiatedFrom)
 
@@ -961,11 +969,11 @@ proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s:
         loadToReplayNodes(g, c.config, c.cache, m, g[int m])
 
     assert g[si].status in {loaded, storing, stored}
-    if not g[si].symsInit:
-      g[si].symsInit = true
-      setLen g[si].syms, g[si].fromDisk.syms.len
+    #if not g[si].symsInit:
+    #  g[si].symsInit = true
+    #  setLen g[si].syms, g[si].fromDisk.syms.len
 
-    if g[si].syms[s.item] == nil:
+    if g[si].syms.getOrDefault(s.item) == nil:
       if g[si].fromDisk.syms[s.item].kind != skModule:
         result = symHeaderFromPacked(c, g, g[si].fromDisk.syms[s.item], si, s.item)
         # store it here early on, so that recursions work properly:
@@ -1012,11 +1020,11 @@ proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t
     assert g[si].status in {loaded, storing, stored}
     assert t.item > 0
 
-    if not g[si].typesInit:
-      g[si].typesInit = true
-      setLen g[si].types, g[si].fromDisk.types.len
+    #if not g[si].typesInit:
+    #  g[si].typesInit = true
+    #  setLen g[si].types, g[si].fromDisk.types.len
 
-    if g[si].types[t.item] == nil:
+    if g[si].types.getOrDefault(t.item) == nil:
       result = typeHeaderFromPacked(c, g, g[si].fromDisk.types[t.item], si, t.item)
       # store it here early on, so that recursions work properly:
       g[si].types[t.item] = result
@@ -1155,10 +1163,7 @@ proc loadProcBody*(config: ConfigRef, cache: IdentCache;
 proc loadTypeFromId*(config: ConfigRef, cache: IdentCache;
                      g: var PackedModuleGraph; module: int; id: PackedItemId): PType =
   bench g.loadType:
-    if id.item < g[module].types.len:
-      result = g[module].types[id.item]
-    else:
-      result = nil
+    result = g[module].types.getOrDefault(id.item)
     if result == nil:
       var decoder = PackedDecoder(
         lastModule: int32(-1),
@@ -1171,10 +1176,7 @@ proc loadTypeFromId*(config: ConfigRef, cache: IdentCache;
 proc loadSymFromId*(config: ConfigRef, cache: IdentCache;
                     g: var PackedModuleGraph; module: int; id: PackedItemId): PSym =
   bench g.loadSym:
-    if id.item < g[module].syms.len:
-      result = g[module].syms[id.item]
-    else:
-      result = nil
+    result = g[module].syms.getOrDefault(id.item)
     if result == nil:
       var decoder = PackedDecoder(
         lastModule: int32(-1),
@@ -1190,19 +1192,6 @@ proc translateId*(id: PackedItemId; g: PackedModuleGraph; thisModule: int; confi
   else:
     ItemId(module: toFileIndex(id.module, g[thisModule].fromDisk, config).int32, item: id.item)
 
-proc checkForHoles(m: PackedModule; config: ConfigRef; moduleId: int) =
-  var bugs = 0
-  for i in 1 .. high(m.syms):
-    if m.syms[i].kind == skUnknown:
-      echo "EMPTY ID ", i, " module ", moduleId, " ", toFullPath(config, FileIndex(moduleId))
-      inc bugs
-  assert bugs == 0
-  when false:
-    var nones = 0
-    for i in 1 .. high(m.types):
-      inc nones, m.types[i].kind == tyNone
-    assert nones < 1
-
 proc simulateLoadedModule*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
                            moduleSym: PSym; m: PackedModule) =
   # For now only used for heavy debugging. In the future we could use this to reduce the
diff --git a/compiler/nir/nirlineinfos.nim b/compiler/ic/iclineinfos.nim
index f11ef7c42..74a7d971b 100644
--- a/compiler/nir/nirlineinfos.nim
+++ b/compiler/ic/iclineinfos.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nim Compiler
-#        (c) Copyright 2023 Andreas Rumpf
+#        (c) Copyright 2024 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
diff --git a/compiler/ic/integrity.nim b/compiler/ic/integrity.nim
index d78e56847..3e8ea2503 100644
--- a/compiler/ic/integrity.nim
+++ b/compiler/ic/integrity.nim
@@ -10,7 +10,7 @@
 ## Integrity checking for a set of .rod files.
 ## The set must cover a complete Nim project.
 
-import std/sets
+import std/[sets, tables]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -108,18 +108,18 @@ proc checkModule(c: var CheckedContext; m: PackedModule) =
   # We check that:
   # - Every symbol references existing types and symbols.
   # - Every tree node references existing types and symbols.
-  for i in 0..high(m.syms):
-    checkLocalSym c, int32(i)
+  for _, v in pairs(m.syms):
+    checkLocalSym c, v.id
 
   checkTree c, m.toReplay
   checkTree c, m.topLevel
 
   for e in m.exports:
-    assert e[1] >= 0 and e[1] < m.syms.len
+    #assert e[1] >= 0 and e[1] < m.syms.len
     assert e[0] == m.syms[e[1]].name
 
   for e in m.compilerProcs:
-    assert e[1] >= 0 and e[1] < m.syms.len
+    #assert e[1] >= 0 and e[1] < m.syms.len
     assert e[0] == m.syms[e[1]].name
 
   checkLocalSymIds c, m, m.converters
diff --git a/compiler/ic/navigator.nim b/compiler/ic/navigator.nim
index da8e7e597..39037b94f 100644
--- a/compiler/ic/navigator.nim
+++ b/compiler/ic/navigator.nim
@@ -11,7 +11,7 @@
 ## IDE-like features. It uses the set of .rod files to accomplish
 ## its task. The set must cover a complete Nim project.
 
-import std/sets
+import std/[sets, tables]
 
 from std/os import nil
 from std/private/miscdollars import toLocation
@@ -20,7 +20,7 @@ when defined(nimPreviewSlimSystem):
   import std/assertions
 
 import ".." / [ast, modulegraphs, msgs, options]
-import ".." / nir / nirlineinfos
+import iclineinfos
 import packed_ast, bitabs, ic
 
 type
diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim
index 2599e07d1..a39bb7adf 100644
--- a/compiler/ic/packed_ast.nim
+++ b/compiler/ic/packed_ast.nim
@@ -16,7 +16,7 @@ import std/[hashes, tables, strtabs]
 import bitabs, rodfiles
 import ".." / [ast, options]
 
-import ".." / nir / nirlineinfos
+import iclineinfos
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -47,6 +47,7 @@ type
     path*: NodeId
 
   PackedSym* = object
+    id*: int32
     kind*: TSymKind
     name*: LitId
     typ*: PackedItemId
@@ -71,6 +72,7 @@ type
     instantiatedFrom*: PackedItemId
 
   PackedType* = object
+    id*: int32
     kind*: TTypeKind
     callConv*: TCallingConvention
     #nodekind*: TNodeKind
diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim
index 5eef3874a..ac995dd2e 100644
--- a/compiler/ic/rodfiles.nim
+++ b/compiler/ic/rodfiles.nim
@@ -19,6 +19,8 @@ from std/typetraits import supportsCopyMem
 when defined(nimPreviewSlimSystem):
   import std/[syncio, assertions]
 
+import std / tables
+
 ## Overview
 ## ========
 ## `RodFile` represents a Rod File (versioned binary format), and the
@@ -170,6 +172,18 @@ proc storeSeq*[T](f: var RodFile; s: seq[T]) =
     for i in 0..<s.len:
       storePrim(f, s[i])
 
+proc storeOrderedTable*[K, T](f: var RodFile; s: OrderedTable[K, T]) =
+  if f.err != ok: return
+  if s.len >= high(int32):
+    setError f, tooBig
+    return
+  var lenPrefix = int32(s.len)
+  if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
+    setError f, ioFailure
+  else:
+    for _, v in s:
+      storePrim(f, v)
+
 proc loadPrim*(f: var RodFile; s: var string) =
   ## Read a string, the length was stored as a prefix
   if f.err != ok: return
@@ -211,6 +225,19 @@ proc loadSeq*[T](f: var RodFile; s: var seq[T]) =
     for i in 0..<lenPrefix:
       loadPrim(f, s[i])
 
+proc loadOrderedTable*[K, T](f: var RodFile; s: var OrderedTable[K, T]) =
+  ## `T` must be compatible with `copyMem`, see `loadPrim`
+  if f.err != ok: return
+  var lenPrefix = int32(0)
+  if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
+    setError f, ioFailure
+  else:
+    s = initOrderedTable[K, T](lenPrefix)
+    for i in 0..<lenPrefix:
+      var x = default T
+      loadPrim(f, x)
+      s[x.id] = x
+
 proc storeHeader*(f: var RodFile; cookie = defaultCookie) =
   ## stores the header which is described by `cookie`.
   if f.err != ok: return
diff --git a/compiler/importer.nim b/compiler/importer.nim
index 176b33b7b..ffb7e0305 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -246,7 +246,8 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool)
     result = createModuleAliasImpl(realModule.name)
   if importHidden:
     result.options.incl optImportHidden
-  c.unusedImports.add((result, n.info))
+  let moduleIdent = if n.kind == nkInfix: n[^1] else: n
+  c.unusedImports.add((result, moduleIdent.info))
   c.importModuleMap[result.id] = realModule.id
   c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id
 
diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim
index 6b7676653..3dcc364a3 100644
--- a/compiler/injectdestructors.nim
+++ b/compiler/injectdestructors.nim
@@ -317,8 +317,9 @@ proc isCriticalLink(dest: PNode): bool {.inline.} =
   ]#
   result = dest.kind != nkSym
 
-proc finishCopy(c: var Con; result, dest: PNode; isFromSink: bool) =
-  if c.graph.config.selectedGC == gcOrc:
+proc finishCopy(c: var Con; result, dest: PNode; flags: set[MoveOrCopyFlag]; isFromSink: bool) =
+  if c.graph.config.selectedGC == gcOrc and IsExplicitSink notin flags:
+    # add cyclic flag, but not to sink calls, which IsExplicitSink generates
     let t = dest.typ.skipTypes(tyUserTypeClasses + {tyGenericInst, tyAlias, tySink, tyDistinct})
     if cyclicType(c.graph, t):
       result.add boolLit(c.graph, result.info, isFromSink or isCriticalLink(dest))
@@ -464,7 +465,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
       var newCall = newTreeIT(nkCall, src.info, src.typ,
             newSymNode(op),
             src)
-      c.finishCopy(newCall, n, isFromSink = true)
+      c.finishCopy(newCall, n, {}, isFromSink = true)
       result.add newTreeI(nkFastAsgn,
           src.info, tmp,
           newCall
@@ -473,7 +474,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
       result.add c.genWasMoved(tmp)
       var m = c.genCopy(tmp, n, {})
       m.add p(n, c, s, normal)
-      c.finishCopy(m, n, isFromSink = true)
+      c.finishCopy(m, n, {}, isFromSink = true)
       result.add m
     if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
       message(c.graph.config, n.info, hintPerformance,
@@ -501,7 +502,7 @@ proc containsConstSeq(n: PNode): bool =
     return true
   result = false
   case n.kind
-  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
+  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast:
     result = containsConstSeq(n[1])
   of nkObjConstr, nkClosure:
     for i in 1..<n.len:
@@ -653,7 +654,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
       for j in 0 ..< it.len-1:
         branch[j] = copyTree(it[j])
       var ofScope = nestedScope(s, it.lastSon)
-      branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
+      branch[^1] = if n.typ.isEmptyType or it[^1].typ.isEmptyType or willProduceStmt:
                      processScope(c, ofScope, maybeVoid(it[^1], ofScope))
                    else:
                      processScopeExpr(c, ofScope, it[^1], processCall, tmpFlags)
@@ -701,7 +702,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
         #Condition needs to be destroyed outside of the condition/branch scope
         branch[0] = p(it[0], c, s, normal)
 
-      branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
+      branch[^1] = if n.typ.isEmptyType or it[^1].typ.isEmptyType or willProduceStmt:
                      processScope(c, branchScope, maybeVoid(it[^1], branchScope))
                    else:
                      processScopeExpr(c, branchScope, it[^1], processCall, tmpFlags)
@@ -761,7 +762,7 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
       let tmp = c.getTemp(s, n[0].typ, n.info)
       var m = c.genCopyNoCheck(tmp, n[0], attachedAsgn)
       m.add p(n[0], c, s, normal)
-      c.finishCopy(m, n[0], isFromSink = false)
+      c.finishCopy(m, n[0], {}, isFromSink = false)
       result = newTree(nkStmtList, c.genWasMoved(tmp), m)
       var toDisarm = n[0]
       if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon
@@ -829,6 +830,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
     elif n.kind in {nkObjDownConv, nkObjUpConv}:
       result = copyTree(n)
       result[0] = p(n[0], c, s, sinkArg)
+    elif n.kind == nkCast and n.typ.skipTypes(abstractInst).kind in {tyString, tySequence}:
+      result = copyTree(n)
+      result[1] = p(n[1], c, s, sinkArg)
     elif n.typ == nil:
       # 'raise X' can be part of a 'case' expression. Deal with it here:
       result = p(n, c, s, normal)
@@ -872,7 +876,8 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
       for i in 1..<n.len:
         if n[i].kind == nkExprColonExpr:
           let field = lookupFieldAgain(t, n[i][0].sym)
-          if field != nil and sfCursor in field.flags:
+          if field != nil and (sfCursor in field.flags or field.typ.kind in {tyOpenArray, tyVarargs}):
+            # don't sink fields with openarray types
             result[i][1] = p(n[i][1], c, s, normal)
           else:
             result[i][1] = p(n[i][1], c, s, m)
@@ -893,7 +898,8 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
       elif c.inSpawn > 0:
         c.inSpawn.dec
 
-      let parameters = n[0].typ
+      # bug #23907; skips tyGenericInst for generic callbacks
+      let parameters = if n[0].typ != nil: n[0].typ.skipTypes(abstractInst) else: n[0].typ
       let L = if parameters != nil: parameters.signatureLen else: 0
 
       when false:
@@ -1168,7 +1174,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
         result = c.genCopy(dest, ri, flags)
         dec c.inEnsureMove, isEnsureMove
         result.add p(ri, c, s, consumed)
-        c.finishCopy(result, dest, isFromSink = false)
+        c.finishCopy(result, dest, flags, isFromSink = false)
     of nkBracket:
       # array constructor
       if ri.len > 0 and isDangerousSeq(ri.typ):
@@ -1176,7 +1182,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
         result = c.genCopy(dest, ri, flags)
         dec c.inEnsureMove, isEnsureMove
         result.add p(ri, c, s, consumed)
-        c.finishCopy(result, dest, isFromSink = false)
+        c.finishCopy(result, dest, flags, isFromSink = false)
       else:
         result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
     of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
@@ -1186,9 +1192,9 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
         # Rule 3: `=sink`(x, z); wasMoved(z)
         let snk = c.genSink(s, dest, ri, flags)
         result = newTree(nkStmtList, snk, c.genWasMoved(ri))
-      elif ri.sym.kind != skParam and ri.sym.owner == c.owner and
-          isLastRead(ri, c, s) and canBeMoved(c, dest.typ) and not isCursor(ri) and
-          not ({sfGlobal, sfPure} <= ri.sym.flags):
+      elif ri.sym.kind != skParam and
+          isAnalysableFieldAccess(ri, c.owner) and
+          isLastRead(ri, c, s) and canBeMoved(c, dest.typ):
         # Rule 3: `=sink`(x, z); wasMoved(z)
         let snk = c.genSink(s, dest, ri, flags)
         result = newTree(nkStmtList, snk, c.genWasMoved(ri))
@@ -1197,7 +1203,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
         result = c.genCopy(dest, ri, flags)
         dec c.inEnsureMove, isEnsureMove
         result.add p(ri, c, s, consumed)
-        c.finishCopy(result, dest, isFromSink = false)
+        c.finishCopy(result, dest, flags, isFromSink = false)
     of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast:
       result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
     of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
@@ -1217,7 +1223,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
         result = c.genCopy(dest, ri, flags)
         dec c.inEnsureMove, isEnsureMove
         result.add p(ri, c, s, consumed)
-        c.finishCopy(result, dest, isFromSink = false)
+        c.finishCopy(result, dest, flags, isFromSink = false)
 
 when false:
   proc computeUninit(c: var Con) =
diff --git a/compiler/installer.ini b/compiler/installer.ini
index 8569d0ef8..54a35dbee 100644
--- a/compiler/installer.ini
+++ b/compiler/installer.ini
@@ -6,11 +6,11 @@ Name: "Nim"
 Version: "$version"
 Platforms: """
   windows: i386;amd64
-  linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64
+  linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64;loongarch64
   macosx: i386;amd64;powerpc64;arm64
   solaris: i386;amd64;sparc;sparc64
   freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el
-  netbsd: i386;amd64
+  netbsd: i386;amd64;arm64
   openbsd: i386;amd64;arm;arm64
   dragonfly: i386;amd64
   crossos: amd64
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index baebfe188..713944def 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -33,7 +33,7 @@ import
   nversion, msgs, idents, types,
   ropes, wordrecg, renderer,
   cgmeth, lowerings, sighashes, modulegraphs, lineinfos,
-  transf, injectdestructors, sourcemap, astmsgs, backendpragmas,
+  transf, injectdestructors, sourcemap, astmsgs, pushpoppragmas,
   mangleutils
 
 import pipelineutils
@@ -103,7 +103,7 @@ type
     prc: PSym
     globals, locals, body: Rope
     options: TOptions
-    optionsStack: seq[TOptions]
+    optionsStack: seq[(TOptions, TNoteKinds)]
     module: BModule
     g: PGlobals
     beforeRetNeeded: bool
@@ -111,13 +111,25 @@ type
     blocks: seq[TBlock]
     extraIndent: int
     previousFileName: string  # For frameInfo inside templates.
+    # legacy: generatedParamCopies and up fields are used for jsNoLambdaLifting
+    generatedParamCopies: IntSet
+    up: PProc     # up the call chain; required for closure support
 
 template config*(p: PProc): ConfigRef = p.module.config
 
 proc indentLine(p: PProc, r: Rope): Rope =
   var p = p
-  let ind = p.blocks.len + p.extraIndent
-  result = repeat(' ', ind*2) & r
+  if jsNoLambdaLifting in p.config.legacyFeatures:
+    var ind = 0
+    while true:
+      inc ind, p.blocks.len + p.extraIndent
+      if p.up == nil or p.up.prc != p.prc.owner:
+        break
+      p = p.up
+    result = repeat(' ', ind*2) & r
+  else:
+    let ind = p.blocks.len + p.extraIndent
+    result = repeat(' ', ind*2) & r
 
 template line(p: PProc, added: string) =
   p.body.add(indentLine(p, rope(added)))
@@ -183,7 +195,7 @@ proc mapType(typ: PType): TJSTypeKind =
   of tyPointer:
     # treat a tyPointer like a typed pointer to an array of bytes
     result = etyBaseIndex
-  of tyRange, tyDistinct, tyOrdinal, tyProxy, tyLent:
+  of tyRange, tyDistinct, tyOrdinal, tyError, tyLent:
     # tyLent is no-op as JS has pass-by-reference semantics
     result = mapType(skipModifier t)
   of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt
@@ -234,7 +246,7 @@ proc mangleName(m: BModule, s: PSym): Rope =
     for chr in name:
       if chr notin {'A'..'Z','a'..'z','_','$','0'..'9'}:
         return false
-  result = s.loc.r
+  result = s.loc.snippet
   if result == "":
     if s.kind == skField and s.name.s.validJsName:
       result = rope(s.name.s)
@@ -265,7 +277,7 @@ proc mangleName(m: BModule, s: PSym): Rope =
       else:
         result.add("_")
         result.add(rope(s.id))
-    s.loc.r = result
+    s.loc.snippet = result
 
 proc escapeJSString(s: string): string =
   result = newStringOfCap(s.len + s.len shr 2)
@@ -599,6 +611,17 @@ template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
   r.res = frmt % [a, tmp]
   r.kind = resExpr
 
+proc genBreakState(p: PProc, n: PNode, r: var TCompRes) =
+  var a: TCompRes = default(TCompRes)
+  # mangle `:state` properly somehow
+  if n.kind == nkClosure:
+    gen(p, n[1], a)
+    r.res = "(($1).HEX3Astate < 0)" % [rdLoc(a)]
+  else:
+    gen(p, n, a)
+    r.res = "((($1.ClE_0).HEX3Astate) < 0)" % [rdLoc(a)]
+  r.kind = resExpr
+
 proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
   var
     x, y: TCompRes = default(TCompRes)
@@ -969,7 +992,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
         # if isJsObject(throwObj.typ):
         if isImportedException(throwObj.typ, p.config):
           orExpr.addf("lastJSError instanceof $1",
-            [throwObj.typ.sym.loc.r])
+            [throwObj.typ.sym.loc.snippet])
         else:
           orExpr.addf("isObj(lastJSError.m_type, $1)",
                [genTypeInfo(p, throwObj.typ)])
@@ -979,8 +1002,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
       # If some branch requires a local alias introduce it here. This is needed
       # since JS cannot do ``catch x as y``.
       if excAlias != nil:
-        excAlias.sym.loc.r = mangleName(p.module, excAlias.sym)
-        lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.r)
+        excAlias.sym.loc.snippet = mangleName(p.module, excAlias.sym)
+        lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.snippet)
       gen(p, n[i][^1], a)
       moveInto(p, a, r)
       lineF(p, "}$n", [])
@@ -1086,14 +1109,19 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
           lineF(p, "break;$n", [])
     of nkElse:
       if transferRange:
-         lineF(p, "else{$n", [])
+        if n.len == 2: # a dangling else for a case statement
+          transferRange = false
+          lineF(p, "switch ($1) {$n", [cond.rdLoc])
+          lineF(p, "default: $n", [])
+        else:
+          lineF(p, "else{$n", [])
       else:
         lineF(p, "default: $n", [])
       p.nested:
         gen(p, it[0], stmt)
         moveInto(p, stmt, r)
         if transferRange:
-           lineF(p, "}$n", [])
+          lineF(p, "}$n", [])
         else:
           lineF(p, "break;$n", [])
     else: internalError(p.config, it.info, "jsgen.genCaseStmt")
@@ -1200,12 +1228,13 @@ proc genIf(p: PProc, n: PNode, r: var TCompRes) =
 proc generateHeader(p: PProc, prc: PSym): Rope =
   result = ""
   let typ = prc.typ
-  if typ.callConv == ccClosure:
-    # we treat Env as the `this` parameter of the function
-    # to keep it simple
-    let env = prc.ast[paramsPos].lastSon
-    assert env.kind == nkSym, "env is missing"
-    env.sym.loc.r = "this"
+  if jsNoLambdaLifting notin p.config.legacyFeatures:
+    if typ.callConv == ccClosure:
+      # we treat Env as the `this` parameter of the function
+      # to keep it simple
+      let env = prc.ast[paramsPos].lastSon
+      assert env.kind == nkSym, "env is missing"
+      env.sym.loc.snippet = "this"
 
   for i in 1..<typ.n.len:
     assert(typ.n[i].kind == nkSym)
@@ -1239,9 +1268,10 @@ const
 
 proc needsNoCopy(p: PProc; y: PNode): bool =
   return y.kind in nodeKindsNeedNoCopy or
-        ((mapType(y.typ) != etyBaseIndex) and
+        ((mapType(y.typ) != etyBaseIndex or
+          (jsNoLambdaLifting in p.config.legacyFeatures and y.kind == nkSym and y.sym.kind == skParam)) and
           (skipTypes(y.typ, abstractInst).kind in
-            {tyRef, tyPtr, tyLent, tyVar, tyCstring, tyProc, tyOwned} + IntegralTypes))
+            {tyRef, tyPtr, tyLent, tyVar, tyCstring, tyProc, tyOwned, tyOpenArray} + IntegralTypes))
 
 proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
   var a, b: TCompRes = default(TCompRes)
@@ -1268,7 +1298,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
       lineF(p, "$1 = nimCopy(null, $2, $3);$n",
                [a.rdLoc, b.res, genTypeInfo(p, y.typ)])
   of etyObject:
-    if x.typ.kind in {tyVar, tyLent} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
+    if x.typ.kind in {tyVar, tyLent, tyOpenArray, tyVarargs} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
       lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
     else:
       useMagic(p, "nimCopy")
@@ -1346,8 +1376,8 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
   else:
     if b[1].kind != nkSym: internalError(p.config, b[1].info, "genFieldAddr")
     var f = b[1].sym
-    if f.loc.r == "": f.loc.r = mangleName(p.module, f)
-    r.res = makeJSString($f.loc.r)
+    if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
+    r.res = makeJSString($f.loc.snippet)
   internalAssert p.config, a.typ != etyBaseIndex
   r.address = a.res
   r.kind = resExpr
@@ -1374,8 +1404,8 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
   else:
     if n[1].kind != nkSym: internalError(p.config, n[1].info, "genFieldAccess")
     var f = n[1].sym
-    if f.loc.r == "": f.loc.r = mangleName(p.module, f)
-    r.res = "$1.$2" % [r.res, f.loc.r]
+    if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
+    r.res = "$1.$2" % [r.res, f.loc.snippet]
     mkTemp(1)
   r.kind = resExpr
 
@@ -1395,11 +1425,11 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) =
   # Field symbol
   var field = accessExpr[1].sym
   internalAssert p.config, field.kind == skField
-  if field.loc.r == "": field.loc.r = mangleName(p.module, field)
+  if field.loc.snippet == "": field.loc.snippet = mangleName(p.module, field)
   # Discriminant symbol
   let disc = checkExpr[2].sym
   internalAssert p.config, disc.kind == skField
-  if disc.loc.r == "": disc.loc.r = mangleName(p.module, disc)
+  if disc.loc.snippet == "": disc.loc.snippet = mangleName(p.module, disc)
 
   var setx: TCompRes = default(TCompRes)
   gen(p, checkExpr[1], setx)
@@ -1416,16 +1446,16 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) =
   useMagic(p, "reprDiscriminant") # no need to offset by firstOrd unlike for cgen
   let msg = genFieldDefect(p.config, field.name.s, disc)
   lineF(p, "if ($1[$2.$3]$4undefined) { raiseFieldError2(makeNimstrLit($5), reprDiscriminant($2.$3, $6)); }$n",
-    setx.res, tmp, disc.loc.r, if negCheck: "!==" else: "===",
+    setx.res, tmp, disc.loc.snippet, if negCheck: "!==" else: "===",
     makeJSString(msg), genTypeInfo(p, disc.typ))
 
   if addrTyp != nil and mapType(p, addrTyp) == etyBaseIndex:
     r.typ = etyBaseIndex
-    r.res = makeJSString($field.loc.r)
+    r.res = makeJSString($field.loc.snippet)
     r.address = tmp
   else:
     r.typ = etyNone
-    r.res = "$1.$2" % [tmp, field.loc.r]
+    r.res = "$1.$2" % [tmp, field.loc.snippet]
   r.kind = resExpr
 
 proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
@@ -1456,7 +1486,7 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
   r.kind = resExpr
 
 proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) =
-  var ty = skipTypes(n[0].typ, abstractVarRange)
+  var ty = skipTypes(n[0].typ, abstractVarRange+tyUserTypeClasses)
   if ty.kind in {tyRef, tyPtr, tyLent, tyOwned}: ty = skipTypes(ty.elementType, abstractVarRange)
   case ty.kind
   of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs:
@@ -1492,10 +1522,10 @@ template isIndirect(x: PSym): bool =
 
 proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) =
   let s = n.sym
-  if s.loc.r == "": internalError(p.config, n.info, "genAddr: 3")
+  if s.loc.snippet == "": internalError(p.config, n.info, "genAddr: 3")
   case s.kind
   of skParam:
-    r.res = s.loc.r
+    r.res = s.loc.snippet
     r.address = ""
     r.typ = etyNone
   of skVar, skLet, skResult:
@@ -1509,15 +1539,15 @@ proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) =
       # make addr() a no-op:
       r.typ = etyNone
       if isIndirect(s):
-        r.res = s.loc.r & "[0]"
+        r.res = s.loc.snippet & "[0]"
       else:
-        r.res = s.loc.r
+        r.res = s.loc.snippet
       r.address = ""
     elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex:
       # for ease of code generation, we do not distinguish between
       # sfAddrTaken and sfGlobal.
       r.typ = etyBaseIndex
-      r.address = s.loc.r
+      r.address = s.loc.snippet
       r.res = rope("0")
     else:
       # 'var openArray' for instance produces an 'addr' but this is harmless:
@@ -1590,7 +1620,30 @@ proc attachProc(p: PProc; s: PSym) =
 
 proc genProcForSymIfNeeded(p: PProc, s: PSym) =
   if not p.g.generatedSyms.containsOrIncl(s.id):
-    attachProc(p, s)
+    if jsNoLambdaLifting in p.config.legacyFeatures:
+      let newp = genProc(p, s)
+      var owner = p
+      while owner != nil and owner.prc != s.owner:
+        owner = owner.up
+      if owner != nil: owner.locals.add(newp)
+      else: attachProc(p, newp, s)
+    else:
+      attachProc(p, s)
+
+proc genCopyForParamIfNeeded(p: PProc, n: PNode) =
+  let s = n.sym
+  if p.prc == s.owner or needsNoCopy(p, n):
+    return
+  var owner = p.up
+  while true:
+    if owner == nil:
+      internalError(p.config, n.info, "couldn't find the owner proc of the closed over param: " & s.name.s)
+    if owner.prc == s.owner:
+      if not owner.generatedParamCopies.containsOrIncl(s.id):
+        let copy = "$1 = nimCopy(null, $1, $2);$n" % [s.loc.snippet, genTypeInfo(p, s.typ)]
+        owner.locals.add(owner.indentLine(copy))
+      return
+    owner = owner.up
 
 proc genVarInit(p: PProc, v: PSym, n: PNode)
 
@@ -1598,38 +1651,40 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
   var s = n.sym
   case s.kind
   of skVar, skLet, skParam, skTemp, skResult, skForVar:
-    if s.loc.r == "":
+    if s.loc.snippet == "":
       internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
     if sfCompileTime in s.flags:
       genVarInit(p, s, if s.astdef != nil: s.astdef else: newNodeI(nkEmpty, s.info))
+    if jsNoLambdaLifting in p.config.legacyFeatures and s.kind == skParam:
+      genCopyForParamIfNeeded(p, n)
     let k = mapType(p, s.typ)
     if k == etyBaseIndex:
       r.typ = etyBaseIndex
       if {sfAddrTaken, sfGlobal} * s.flags != {}:
         if isIndirect(s):
-          r.address = "$1[0][0]" % [s.loc.r]
-          r.res = "$1[0][1]" % [s.loc.r]
+          r.address = "$1[0][0]" % [s.loc.snippet]
+          r.res = "$1[0][1]" % [s.loc.snippet]
         else:
-          r.address = "$1[0]" % [s.loc.r]
-          r.res = "$1[1]" % [s.loc.r]
+          r.address = "$1[0]" % [s.loc.snippet]
+          r.res = "$1[1]" % [s.loc.snippet]
       else:
-        r.address = s.loc.r
-        r.res = s.loc.r & "_Idx"
+        r.address = s.loc.snippet
+        r.res = s.loc.snippet & "_Idx"
     elif isIndirect(s):
-      r.res = "$1[0]" % [s.loc.r]
+      r.res = "$1[0]" % [s.loc.snippet]
     else:
-      r.res = s.loc.r
+      r.res = s.loc.snippet
   of skConst:
     genConstant(p, s)
-    if s.loc.r == "":
+    if s.loc.snippet == "":
       internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
-    r.res = s.loc.r
+    r.res = s.loc.snippet
   of skProc, skFunc, skConverter, skMethod, skIterator:
     if sfCompileTime in s.flags:
       localError(p.config, n.info, "request to generate code for .compileTime proc: " &
           s.name.s)
     discard mangleName(p.module, s)
-    r.res = s.loc.r
+    r.res = s.loc.snippet
     if lfNoDecl in s.loc.flags or s.magic notin generatedMagics or
        {sfImportc, sfInfixCall} * s.flags != {}:
       discard
@@ -1641,13 +1696,13 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
     else:
       genProcForSymIfNeeded(p, s)
   else:
-    if s.loc.r == "":
+    if s.loc.snippet == "":
       internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
     if mapType(p, s.typ) == etyBaseIndex:
-      r.address = s.loc.r
-      r.res = s.loc.r & "_Idx"
+      r.address = s.loc.snippet
+      r.res = s.loc.snippet & "_Idx"
     else:
-      r.res = s.loc.r
+      r.res = s.loc.snippet
   r.kind = resVal
 
 proc genDeref(p: PProc, n: PNode, r: var TCompRes) =
@@ -1789,9 +1844,9 @@ proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType;
 proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
   # don't call '$' here for efficiency:
   let f = n[0].sym
-  if f.loc.r == "": f.loc.r = mangleName(p.module, f)
+  if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
   if sfInfixCall in f.flags:
-    let pat = $n[0].sym.loc.r
+    let pat = $n[0].sym.loc.snippet
     internalAssert p.config, pat.len > 0
     if pat.contains({'#', '(', '@'}):
       var typ = skipTypes(n[0].typ, abstractInst)
@@ -1906,7 +1961,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
   of tyInt8..tyInt32, tyUInt8..tyUInt32, tyEnum, tyChar:
     result = putToSeq("0", indirect)
   of tyInt, tyUInt:
-    if $t.sym.loc.r == "bigint":
+    if $t.sym.loc.snippet == "bigint":
       result = putToSeq("0n", indirect)
     else:
       result = putToSeq("0", indirect)
@@ -1966,7 +2021,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
       result = putToSeq("null", indirect)
   of tySequence, tyString:
     result = putToSeq("[]", indirect)
-  of tyCstring, tyProc:
+  of tyCstring, tyProc, tyOpenArray:
     result = putToSeq("null", indirect)
   of tyStatic:
     if t.n != nil:
@@ -2018,7 +2073,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
     gen(p, n, a)
     case mapType(p, v.typ)
     of etyObject, etySeq:
-      if needsNoCopy(p, n):
+      if v.typ.kind in {tyOpenArray, tyVarargs} or needsNoCopy(p, n):
         s = a.res
       else:
         useMagic(p, "nimCopy")
@@ -2028,28 +2083,28 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
       if a.typ == etyBaseIndex:
         if targetBaseIndex:
           line(p, runtimeFormat(varCode & " = $3, $2_Idx = $4;$n",
-                   [returnType, v.loc.r, a.address, a.res]))
+                   [returnType, v.loc.snippet, a.address, a.res]))
         else:
           if isIndirect(v):
             line(p, runtimeFormat(varCode & " = [[$3, $4]];$n",
-                     [returnType, v.loc.r, a.address, a.res]))
+                     [returnType, v.loc.snippet, a.address, a.res]))
           else:
             line(p, runtimeFormat(varCode & " = [$3, $4];$n",
-                     [returnType, v.loc.r, a.address, a.res]))
+                     [returnType, v.loc.snippet, a.address, a.res]))
       else:
         if targetBaseIndex:
           let tmp = p.getTemp
           lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n",
-                   [tmp, a.res, v.loc.r])
+                   [tmp, a.res, v.loc.snippet])
         else:
-          line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, a.res]))
+          line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.snippet, a.res]))
       return
     else:
       s = a.res
     if isIndirect(v):
-      line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.r, s]))
+      line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.snippet, s]))
     else:
-      line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, s]))
+      line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.snippet, s]))
 
   if useReloadingGuard or useGlobalPragmas:
     dec p.extraIndent
@@ -2212,16 +2267,17 @@ proc genDefault(p: PProc, n: PNode; r: var TCompRes) =
   r.res = createVar(p, n.typ, indirect = false)
   r.kind = resExpr
 
-proc genReset(p: PProc, n: PNode) =
+proc genWasMoved(p: PProc, n: PNode) =
+  # TODO: it should be done by nir
   var x: TCompRes = default(TCompRes)
-  useMagic(p, "genericReset")
   gen(p, n[1], x)
   if x.typ == etyBaseIndex:
     lineF(p, "$1 = null, $2 = 0;$n", [x.address, x.res])
   else:
-    let (a, tmp) = maybeMakeTempAssignable(p, n[1], x)
-    lineF(p, "$1 = genericReset($3, $2);$n", [a,
-                  genTypeInfo(p, n[1].typ), tmp])
+    var y: TCompRes = default(TCompRes)
+    genDefault(p, n[1], y)
+    let (a, _) = maybeMakeTempAssignable(p, n[1], x)
+    lineF(p, "$1 = $2;$n", [a, y.rdLoc])
 
 proc genMove(p: PProc; n: PNode; r: var TCompRes) =
   var a: TCompRes = default(TCompRes)
@@ -2229,7 +2285,7 @@ proc genMove(p: PProc; n: PNode; r: var TCompRes) =
   r.res = p.getTemp()
   gen(p, n[1], a)
   lineF(p, "$1 = $2;$n", [r.rdLoc, a.rdLoc])
-  genReset(p, n)
+  genWasMoved(p, n)
   #lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc])
 
 proc genDup(p: PProc; n: PNode; r: var TCompRes) =
@@ -2410,7 +2466,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]")
   of mOf: genOf(p, n, r)
   of mDefault, mZeroDefault: genDefault(p, n, r)
-  of mReset, mWasMoved: genReset(p, n)
+  of mWasMoved: genWasMoved(p, n)
   of mEcho: genEcho(p, n, r)
   of mNLen..mNError, mSlurp, mStaticExec:
     localError(p.config, n.info, errXMustBeCompileTime % n[0].sym.name.s)
@@ -2523,17 +2579,17 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
     let val = it[1]
     gen(p, val, a)
     var f = it[0].sym
-    if f.loc.r == "": f.loc.r = mangleName(p.module, f)
+    if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
     fieldIDs.incl(lookupFieldAgain(n.typ.skipTypes({tyDistinct}), f).id)
 
     let typ = val.typ.skipTypes(abstractInst)
     if a.typ == etyBaseIndex:
-      initList.addf("$#: [$#, $#]", [f.loc.r, a.address, a.res])
+      initList.addf("$#: [$#, $#]", [f.loc.snippet, a.address, a.res])
     else:
       if not needsNoCopy(p, val):
         useMagic(p, "nimCopy")
         a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
-      initList.addf("$#: $#", [f.loc.r, a.res])
+      initList.addf("$#: $#", [f.loc.snippet, a.res])
   let t = skipTypes(n.typ, abstractInst + skipPtrs)
   createObjInitList(p, t, fieldIDs, initList)
   r.res = ("{$1}") % [initList]
@@ -2592,7 +2648,13 @@ proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) =
   let src = skipTypes(n[0].typ, abstractVarRange)
   let dest = skipTypes(n.typ, abstractVarRange)
   if optRangeCheck notin p.options:
-    return
+    if optJsBigInt64 in p.config.globalOptions and
+          dest.kind in {tyUInt..tyUInt32, tyInt..tyInt32} and
+          src.kind in {tyInt64, tyUInt64}:
+      # conversions to Number are kept
+      r.res = "Number($1)" % [r.res]
+    else:
+      discard
   elif dest.kind in {tyUInt..tyUInt64} and checkUnsignedConversions notin p.config.legacyFeatures:
     if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
       r.res = "BigInt.asUintN($1, $2)" % [$(dest.size * 8), r.res]
@@ -2687,6 +2749,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
   #if gVerbosity >= 3:
   #  echo "BEGIN generating code for: " & prc.name.s
   var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options)
+  p.up = oldProc
   var returnStmt: Rope = ""
   var resultAsgn: Rope = ""
   var name = mangleName(p.module, prc)
@@ -2772,9 +2835,9 @@ proc genPragma(p: PProc, n: PNode) =
     case whichPragma(it)
     of wEmit: genAsmOrEmitStmt(p, it[1])
     of wPush:
-      processPushBackendOption(p.optionsStack, p.options, n, i+1)
+      processPushBackendOption(p.config, p.optionsStack, p.options, n, i+1)
     of wPop:
-      processPopBackendOption(p.optionsStack, p.options)
+      processPopBackendOption(p.config, p.optionsStack, p.options)
     else: discard
 
 proc genCast(p: PProc, n: PNode, r: var TCompRes) =
@@ -2910,14 +2973,17 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
     else:
       genCall(p, n, r)
   of nkClosure:
-    let tmp = getTemp(p)
-    var a: TCompRes = default(TCompRes)
-    var b: TCompRes = default(TCompRes)
-    gen(p, n[0], a)
-    gen(p, n[1], b)
-    lineF(p, "$1 = $2.bind($3); $1.ClP_0 = $2; $1.ClE_0 = $3;$n", [tmp, a.rdLoc, b.rdLoc])
-    r.res = tmp
-    r.kind = resVal
+    if jsNoLambdaLifting in p.config.legacyFeatures:
+      gen(p, n[0], r)
+    else:
+      let tmp = getTemp(p)
+      var a: TCompRes = default(TCompRes)
+      var b: TCompRes = default(TCompRes)
+      gen(p, n[0], a)
+      gen(p, n[1], b)
+      lineF(p, "$1 = $2.bind($3); $1.ClP_0 = $2; $1.ClE_0 = $3;$n", [tmp, a.rdLoc, b.rdLoc])
+      r.res = tmp
+      r.kind = resVal
   of nkCurly: genSetConstr(p, n, r)
   of nkBracket: genArrayConstr(p, n, r)
   of nkPar, nkTupleConstr: genTupleConstr(p, n, r)
@@ -2948,7 +3014,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
   of nkLambdaKinds:
     let s = n[namePos].sym
     discard mangleName(p.module, s)
-    r.res = s.loc.r
+    r.res = s.loc.snippet
     if lfNoDecl in s.loc.flags or s.magic notin generatedMagics: discard
     elif not p.g.generatedSyms.containsOrIncl(s.id):
       p.locals.add(genProc(p, s))
@@ -2999,16 +3065,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
   of nkGotoState, nkState:
     globalError(p.config, n.info, "not implemented")
   of nkBreakState:
-    var a: TCompRes = default(TCompRes)
-    if n[0].kind == nkClosure:
-      gen(p, n[0][1], a)
-      let sym = n[0][1].typ[0].n[0].sym
-      r.res = "(($1).$2 < 0)" % [rdLoc(a), mangleName(p.module, sym)]
-    else:
-      gen(p, n[0], a)
-      let sym = n[0].typ[0].n[0].sym
-      r.res = "((($1.ClE_0).$2) < 0)" % [rdLoc(a), mangleName(p.module, sym)]
-    r.kind = resExpr
+    genBreakState(p, n[0], r)
   of nkPragmaBlock: gen(p, n.lastSon, r)
   of nkComesFrom:
     discard "XXX to implement for better stack traces"
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index c32a1c614..54cdfc5bc 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -145,11 +145,13 @@ proc createStateField(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
   result = newSym(skField, getIdent(g.cache, ":state"), idgen, iter, iter.info)
   result.typ = createClosureIterStateType(g, iter, idgen)
 
+template isIterator*(owner: PSym): bool =
+  owner.kind == skIterator and owner.typ.callConv == ccClosure
+
 proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType =
-  # YYY meh, just add the state field for every closure for now, it's too
-  # hard to figure out if it comes from a closure iterator:
   result = createObj(g, idgen, owner, info, final=false)
-  rawAddField(result, createStateField(g, owner, idgen))
+  if owner.isIterator or not isDefined(g.config, "nimOptIters"):
+    rawAddField(result, createStateField(g, owner, idgen))
 
 proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
   if resultPos < iter.ast.len:
@@ -172,26 +174,22 @@ proc addHiddenParam(routine: PSym, param: PSym) =
   assert sfFromGeneric in param.flags
   #echo "produced environment: ", param.id, " for ", routine.id
 
-proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
+proc getEnvParam*(routine: PSym): PSym =
   let params = routine.ast[paramsPos]
   let hidden = lastSon(params)
   if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName:
     result = hidden.sym
     assert sfFromGeneric in result.flags
   else:
+    result = nil
+
+proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
+  result = getEnvParam(routine)
+  if result.isNil:
     # writeStackTrace()
     localError(g.config, routine.info, "internal error: could not find env param for " & routine.name.s)
     result = routine
 
-proc getEnvParam*(routine: PSym): PSym =
-  let params = routine.ast[paramsPos]
-  let hidden = lastSon(params)
-  if hidden.kind == nkSym and hidden.sym.name.s == paramName:
-    result = hidden.sym
-    assert sfFromGeneric in result.flags
-  else:
-    result = nil
-
 proc interestingVar(s: PSym): bool {.inline.} =
   result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and
     sfGlobal notin s.flags and
@@ -231,13 +229,16 @@ proc makeClosure*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; env: PNode; inf
     prc.flags.incl sfInjectDestructors
 
 proc interestingIterVar(s: PSym): bool {.inline.} =
+  # unused with -d:nimOptIters
   # XXX optimization: Only lift the variable if it lives across
   # yield/return boundaries! This can potentially speed up
   # closure iterators quite a bit.
   result = s.kind in {skResult, skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
 
-template isIterator*(owner: PSym): bool =
-  owner.kind == skIterator and owner.typ.callConv == ccClosure
+template liftingHarmful(conf: ConfigRef; owner: PSym): bool =
+  ## lambda lifting can be harmful for JS-like code generators.
+  let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
+  jsNoLambdaLifting in conf.legacyFeatures and conf.backend == backendJs and not isCompileTime
 
 proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen: IdGenerator; owner: PSym) =
   if owner.kind != skMacro:
@@ -255,11 +256,11 @@ proc genCreateEnv(env: PNode): PNode =
 
 proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
   # transforms  (iter)  to  (let env = newClosure[iter](); (iter, env))
+  if liftingHarmful(g.config, owner): return n
   let iter = n.sym
   assert iter.isIterator
 
-  result = newNodeIT(nkStmtListExpr, n.info, n.typ)
-
+  result = newNodeIT(nkStmtListExpr, n.info, iter.typ)
   let hp = getHiddenParam(g, iter)
   var env: PNode
   if owner.isIterator:
@@ -281,6 +282,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
   result.add makeClosure(g, idgen, iter, env, n.info)
 
 proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: PSym): PNode =
+  # unused with -d:nimOptIters
   let envParam = getHiddenParam(g, owner)
   let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr})
   let field = addField(obj, s, g.cache, idgen)
@@ -298,8 +300,8 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
     localError(g.config, n.info,
       ("'$1' is of type <$2> which cannot be captured as it would violate memory" &
        " safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." &
-       " Consider using a <ref $2> which can be captured.") %
-      [s.name.s, typeToString(s.typ), g.config$s.info])
+       " Consider using a <ref T> which can be captured.") %
+      [s.name.s, typeToString(s.typ.skipTypes({tyVar})), g.config$s.info])
   elif not (owner.typ.isClosure or owner.isNimcall and not owner.isExplicitCallConv or isEnv):
     localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
       [s.name.s, owner.name.s, $owner.typ.callConv])
@@ -336,9 +338,13 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
                         info: TLineInfo): PType =
   result = c.ownerToType.getOrDefault(owner.id)
   if result.isNil:
-    result = newType(tyRef, c.idgen, owner)
-    let obj = createEnvObj(c.graph, c.idgen, owner, info)
-    rawAddSon(result, obj)
+    let env = getEnvParam(owner)
+    if env.isNil or not owner.isIterator or not isDefined(c.graph.config, "nimOptIters"):
+      result = newType(tyRef, c.idgen, owner)
+      let obj = createEnvObj(c.graph, c.idgen, owner, info)
+      rawAddSon(result, obj)
+    else:
+      result = env.typ
     c.ownerToType[owner.id] = result
 
 proc asOwnedRef(c: var DetectionPass; t: PType): PType =
@@ -454,7 +460,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
       if owner.isIterator:
         c.somethingToDo = true
         addClosureParam(c, owner, n.info)
-        if interestingIterVar(s):
+        if not isDefined(c.graph.config, "nimOptIters") and interestingIterVar(s):
           if not c.capturedVars.contains(s.id):
             if not c.inTypeOf: c.capturedVars.incl(s.id)
             let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
@@ -768,7 +774,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
     elif s.id in d.capturedVars:
       if s.owner != owner:
         result = accessViaEnvParam(d.graph, n, owner)
-      elif owner.isIterator and interestingIterVar(s):
+      elif owner.isIterator and not isDefined(d.graph.config, "nimOptIters") and interestingIterVar(s):
         result = accessViaEnvParam(d.graph, n, owner)
       else:
         result = accessViaEnvVar(n, owner, d, c)
@@ -880,12 +886,16 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool;
                   idgen: IdGenerator; flags: TransformFlags): PNode =
   let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro
 
-  if body.kind == nkEmpty or
+  if body.kind == nkEmpty or (jsNoLambdaLifting in g.config.legacyFeatures and
+      g.config.backend == backendJs and not isCompileTime) or
       (fn.skipGenericOwner.kind != skModule and force notin flags):
 
     # ignore forward declaration:
     result = body
     tooEarly = true
+    if fn.isIterator and isDefined(g.config, "nimOptIters"):
+      var d = initDetectionPass(g, fn, idgen)
+      addClosureParam(d, fn, body.info)
   else:
     var d = initDetectionPass(g, fn, idgen)
     detectCapturedVars(body, fn, d)
@@ -940,6 +950,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
           break
         ...
     """
+  if liftingHarmful(g.config, owner): return body
   if not (body.kind == nkForStmt and body[^2].kind in nkCallKinds):
     localError(g.config, body.info, "ignored invalid for loop")
     return body
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim
index 350b4cc25..9ff5c0a9d 100644
--- a/compiler/liftdestructors.nim
+++ b/compiler/liftdestructors.nim
@@ -40,7 +40,7 @@ template asink*(t: PType): PSym = getAttachedOp(c.g, t, attachedSink)
 
 proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
 proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
-              info: TLineInfo; idgen: IdGenerator): PSym
+              info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym
 
 proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
                          idgen: IdGenerator)
@@ -157,7 +157,7 @@ proc genWasMovedCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
   result.add(newSymNode(op))
   result.add genAddr(c, x)
 
-proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) =
+proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool, enforceWasMoved = false) =
   case n.kind
   of nkSym:
     if c.filterDiscriminator != nil: return
@@ -167,6 +167,8 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool)
         enforceDefaultOp:
       defaultOp(c, f.typ, body, x.dotField(f), b)
     else:
+      if enforceWasMoved:
+        body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x.dotField(f))
       fillBody(c, f.typ, body, x.dotField(f), b)
   of nkNilLit: discard
   of nkRecCase:
@@ -205,7 +207,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool)
       branch[^1] = newNodeI(nkStmtList, c.info)
 
       fillBodyObj(c, n[i].lastSon, branch[^1], x, y,
-                  enforceDefaultOp = localEnforceDefaultOp)
+                  enforceDefaultOp = localEnforceDefaultOp, enforceWasMoved = c.kind == attachedAsgn)
       if branch[^1].len == 0: inc emptyBranches
       caseStmt.add(branch)
     if emptyBranches != n.len-1:
@@ -216,13 +218,22 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool)
       fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false)
     c.filterDiscriminator = oldfilterDiscriminator
   of nkRecList:
-    for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp)
+    for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp, enforceWasMoved)
   else:
     illFormedAstLocal(n, c.g.config)
 
 proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) =
   if t.baseClass != nil:
-    fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, x, y)
+    let dest = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
+    dest.add newNodeI(nkEmpty, c.info)
+    dest.add x
+    var src = y
+    if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}:
+      src = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
+      src.add newNodeI(nkEmpty, c.info)
+      src.add y
+
+    fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, dest, src)
   fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
 
 proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
@@ -279,6 +290,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
     c.kind = attachedDestructor
     fillBodyObjTImpl(c, t, body, blob, y)
     c.kind = prevKind
+
   else:
     fillBodyObjTImpl(c, t, body, x, y)
 
@@ -1034,7 +1046,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     else:
       discard "cannot copy openArray"
 
-  of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
+  of tyFromExpr, tyError, tyBuiltInTypeClass, tyUserTypeClass,
      tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
      tyGenericParam, tyGenericBody, tyNil, tyUntyped, tyTyped,
      tyTypeDesc, tyGenericInvocation, tyForward, tyStatic:
@@ -1051,7 +1063,9 @@ proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
   assert typ.kind == tyDistinct
   let baseType = typ.elementType
   if getAttachedOp(g, baseType, kind) == nil:
-    discard produceSym(g, c, baseType, kind, info, idgen)
+    # TODO: fixme `isDistinct` is a fix for #23552; remove it after
+    # `-d:nimPreviewNonVarDestructor` becomes the default
+    discard produceSym(g, c, baseType, kind, info, idgen, isDistinct = true)
   result = getAttachedOp(g, baseType, kind)
   setAttachedOp(g, idgen.module, typ, kind, result)
 
@@ -1090,7 +1104,7 @@ proc symDupPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttache
   incl result.flags, sfGeneratedOp
 
 proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
-              info: TLineInfo; idgen: IdGenerator; isDiscriminant = false): PSym =
+              info: TLineInfo; idgen: IdGenerator; isDiscriminant = false; isDistinct = false): PSym =
   if kind == attachedDup:
     return symDupPrototype(g, typ, owner, kind, info, idgen)
 
@@ -1101,7 +1115,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
                    idgen, result, info)
 
   if kind == attachedDestructor and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
-     ((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or typ.kind in {tyRef, tyString, tySequence}):
+     ((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence} and not isDistinct)):
     dest.typ = typ
   else:
     dest.typ = makeVarType(typ.owner, typ, idgen)
@@ -1143,13 +1157,13 @@ proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   body.add newAsgnStmt(xx, yy)
 
 proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
-              info: TLineInfo; idgen: IdGenerator): PSym =
+              info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym =
   if typ.kind == tyDistinct:
     return produceSymDistinctType(g, c, typ, kind, info, idgen)
 
   result = getAttachedOp(g, typ, kind)
   if result == nil:
-    result = symPrototype(g, typ, typ.owner, kind, info, idgen)
+    result = symPrototype(g, typ, typ.owner, kind, info, idgen, isDistinct = isDistinct)
 
   var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType: typ, idgen: idgen,
                    fn: result)
diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim
index dc0b6c360..94a483299 100644
--- a/compiler/lineinfos.nim
+++ b/compiler/lineinfos.nim
@@ -92,9 +92,10 @@ type
     warnStmtListLambda = "StmtListLambda",
     warnBareExcept = "BareExcept",
     warnImplicitDefaultValue = "ImplicitDefaultValue",
-    warnGenericsIgnoredInjection = "GenericsIgnoredInjection",
+    warnIgnoredSymbolInjection = "IgnoredSymbolInjection",
     warnStdPrefix = "StdPrefix"
     warnUser = "User",
+    warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary",
     # hints
     hintSuccess = "Success", hintSuccessX = "SuccessX",
     hintCC = "CC",
@@ -197,9 +198,10 @@ const
     warnStmtListLambda: "statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead",
     warnBareExcept: "$1",
     warnImplicitDefaultValue: "$1",
-    warnGenericsIgnoredInjection: "$1",
+    warnIgnoredSymbolInjection: "$1",
     warnStdPrefix: "$1 needs the 'std' prefix",
     warnUser: "$1",
+    warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable",
     hintSuccess: "operation successful: $#",
     # keep in sync with `testament.isSuccess`
     hintSuccessX: "$build\n$loc lines; ${sec}s; $mem; proj: $project; out: $output",
@@ -266,6 +268,7 @@ const
   NotesVerbosity* = computeNotesVerbosity()
   errXMustBeCompileTime* = "'$1' can only be used in compile-time context"
   errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected"
+  errFloatToString* = "cannot convert '$1' to '$2'"
 
 type
   TFileInfo* = object
diff --git a/compiler/llstream.nim b/compiler/llstream.nim
index fa223b373..cc8148483 100644
--- a/compiler/llstream.nim
+++ b/compiler/llstream.nim
@@ -68,6 +68,7 @@ when not declared(readLineFromStdin):
   # fallback implementation:
   proc readLineFromStdin(prompt: string, line: var string): bool =
     stdout.write(prompt)
+    stdout.flushFile()
     result = readLine(stdin, line)
     if not result:
       stdout.write("\n")
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index e6b4c8f9a..d8fcf73e0 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -50,7 +50,7 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
         case x.kind
         of nkIdent: id.add(x.ident.s)
         of nkSym: id.add(x.sym.name.s)
-        of nkSymChoices:
+        of nkSymChoices, nkOpenSym:
           if x[0].kind == nkSym:
             id.add(x[0].sym.name.s)
           else:
@@ -63,6 +63,8 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
       result = n[0].sym.name
     else:
       handleError(n, origin)
+  of nkOpenSym:
+    result = considerQuotedIdent(c, n[0], origin)
   else:
     handleError(n, origin)
 
@@ -561,23 +563,33 @@ proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
   var amb: bool = false
   discard errorUseQualifier(c, info, s, amb)
 
-proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]; prefix = "use one of") =
-  var err = "ambiguous identifier: '" & candidates[0].name.s & "'"
+proc ambiguousIdentifierMsg*(candidates: seq[PSym], prefix = "use one of", indent = 0): string =
+  result = ""
+  for i in 0 ..< indent:
+    result.add(' ')
+  result.add "ambiguous identifier: '" & candidates[0].name.s & "'"
   var i = 0
   for candidate in candidates:
-    if i == 0: err.add " -- $1 the following:\n" % prefix
-    else: err.add "\n"
-    err.add "  " & candidate.owner.name.s & "." & candidate.name.s
-    err.add ": " & typeToString(candidate.typ)
+    if i == 0: result.add " -- $1 the following:\n" % prefix
+    else: result.add "\n"
+    for i in 0 ..< indent:
+      result.add(' ')
+    result.add "  " & candidate.owner.name.s & "." & candidate.name.s
+    result.add ": " & typeToString(candidate.typ)
     inc i
-  localError(c.config, info, errGenerated, err)
 
-proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) =
+proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]) =
+  localError(c.config, info, errGenerated, ambiguousIdentifierMsg(candidates))
+
+proc ambiguousIdentifierMsg*(choices: PNode, indent = 0): string =
   var candidates = newSeq[PSym](choices.len)
   let prefix = if choices[0].typ.kind != tyProc: "use one of" else: "you need a helper proc to disambiguate"
   for i, n in choices:
     candidates[i] = n.sym
-  errorUseQualifier(c, info, candidates, prefix)
+  result = ambiguousIdentifierMsg(candidates, prefix, indent)
+
+proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) =
+  localError(c.config, info, errGenerated, ambiguousIdentifierMsg(choices))
 
 proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extra = "") =
   var err: string
@@ -630,9 +642,10 @@ type
 
 const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
 
-proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind]): seq[PSym] =
+proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind],
+                       includePureEnum = false): seq[PSym] =
   result = searchInScopesFilterBy(c, ident, filter)
-  if result.len == 0:
+  if skEnumField in filter and (result.len == 0 or includePureEnum):
     result.add allPureEnumFields(c, ident)
 
 proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
@@ -665,24 +678,33 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
     c.isAmbiguous = amb
   of nkSym:
     result = n.sym
+  of nkOpenSym:
+    result = qualifiedLookUp(c, n[0], flags)
   of nkDotExpr:
     result = nil
     var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule})
     if m != nil and m.kind == skModule:
       var ident: PIdent = nil
-      if n[1].kind == nkIdent:
-        ident = n[1].ident
-      elif n[1].kind == nkAccQuoted:
+      if n[1].kind == nkAccQuoted:
         ident = considerQuotedIdent(c, n[1])
+      else:
+        # this includes sym and symchoice nodes, but since we are looking in
+        # a module, it shouldn't matter what was captured
+        ident = n[1].getPIdent
       if ident != nil:
         if m == c.module:
-          result = strTableGet(c.topLevelScope.symbols, ident)
+          var ti: TIdentIter = default(TIdentIter)
+          result = initIdentIter(ti, c.topLevelScope.symbols, ident)
+          if result != nil and nextIdentIter(ti, c.topLevelScope.symbols) != nil:
+            # another symbol exists with same name
+            c.isAmbiguous = true
         else:
+          var amb: bool = false
           if c.importModuleLookup.getOrDefault(m.name.id).len > 1:
-            var amb: bool = false
             result = errorUseQualifier(c, n.info, m, amb)
           else:
-            result = someSym(c.graph, m, ident)
+            result = someSymAmb(c.graph, m, ident, amb)
+            if amb: c.isAmbiguous = true
         if result == nil and checkUndeclared in flags:
           result = errorUndeclaredIdentifierHint(c, ident, n[1].info)
       elif n[1].kind == nkSym:
@@ -701,6 +723,10 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
     if result != nil and result.kind == skStub: loadStub(result)
 
 proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
+  if n.kind == nkOpenSym:
+    # maybe the logic in semexprs should be mirrored here instead
+    # for now it only seems this is called for `pickSym` in `getTypeIdent` 
+    return initOverloadIter(o, c, n[0])
   o.importIdx = -1
   o.marked = initIntSet()
   case n.kind
diff --git a/compiler/main.nim b/compiler/main.nim
index 0b74162a9..4c52317cf 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -22,8 +22,6 @@ import
   modules,
   modulegraphs, lineinfos, pathutils, vmprofiler
 
-# ensure NIR compiles:
-import nir / nir
 
 when defined(nimPreviewSlimSystem):
   import std/[syncio, assertions]
@@ -48,9 +46,6 @@ proc writeDepsFile(g: ModuleGraph) =
       f.writeLine(toFullPath(g.config, k))
   f.close()
 
-proc writeNinjaFile(g: ModuleGraph) =
-  discard "to implement"
-
 proc writeCMakeDepsFile(conf: ConfigRef) =
   ## write a list of C files for build systems like CMake.
   ## only updated when the C file list changes.
@@ -161,26 +156,6 @@ proc commandCompileToC(graph: ModuleGraph) =
     if optGenCDeps in graph.config.globalOptions:
       writeCMakeDepsFile(conf)
 
-proc commandCompileToNir(graph: ModuleGraph) =
-  let conf = graph.config
-  extccomp.initVars(conf)
-  if conf.symbolFiles == disabledSf:
-    if {optRun, optForceFullMake} * conf.globalOptions == {optRun}:
-      if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile):
-        # nothing changed
-        graph.config.notes = graph.config.mainPackageNotes
-        return
-
-  if not extccomp.ccHasSaneOverflow(conf):
-    conf.symbols.defineSymbol("nimEmulateOverflowChecks")
-
-  if conf.symbolFiles == disabledSf:
-    setPipeLinePass(graph, NirPass)
-  else:
-    setPipeLinePass(graph, SemPass)
-  compilePipelineProject(graph)
-  writeNinjaFile(graph)
-
 proc commandJsonScript(graph: ModuleGraph) =
   extccomp.runJsonBuildInstructions(graph.config, graph.config.jsonBuildInstructionsFile)
 
@@ -197,16 +172,13 @@ proc commandCompileToJS(graph: ModuleGraph) =
     if optGenScript in conf.globalOptions:
       writeDepsFile(graph)
 
-proc commandInteractive(graph: ModuleGraph; useNir: bool) =
+proc commandInteractive(graph: ModuleGraph) =
   graph.config.setErrorMaxHighMaybe
   initDefines(graph.config.symbols)
-  if useNir:
-    defineSymbol(graph.config.symbols, "noSignalHandler")
-  else:
-    defineSymbol(graph.config.symbols, "nimscript")
+  defineSymbol(graph.config.symbols, "nimscript")
   # note: seems redundant with -d:nimHasLibFFI
   when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
-  setPipeLinePass(graph, if useNir: NirReplPass else: InterpreterPass)
+  setPipeLinePass(graph, InterpreterPass)
   compilePipelineSystemModule(graph)
   if graph.config.commandArgs.len > 0:
     discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
@@ -293,8 +265,6 @@ proc mainCommand*(graph: ModuleGraph) =
         # and it has added this define implictly, so we must undo that here.
         # A better solution might be to fix system.nim
         undefSymbol(conf.symbols, "useNimRtl")
-    of backendNir:
-      if conf.exc == excNone: conf.exc = excGoto
     of backendInvalid: raiseAssert "unreachable"
 
   proc compileToBackend() =
@@ -305,7 +275,6 @@ proc mainCommand*(graph: ModuleGraph) =
     of backendCpp: commandCompileToC(graph)
     of backendObjc: commandCompileToC(graph)
     of backendJs: commandCompileToJS(graph)
-    of backendNir: commandCompileToNir(graph)
     of backendInvalid: raiseAssert "unreachable"
 
   template docLikeCmd(body) =
@@ -444,7 +413,7 @@ proc mainCommand*(graph: ModuleGraph) =
     wantMainModule(conf)
     commandView(graph)
     #msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!")
-  of cmdInteractive: commandInteractive(graph, isDefined(conf, "nir"))
+  of cmdInteractive: commandInteractive(graph)
   of cmdNimscript:
     if conf.projectIsCmd or conf.projectIsStdin: discard
     elif not fileExists(conf.projectFull):
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index 75f3a3c70..77762d23a 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -11,7 +11,7 @@
 ## represents a complete Nim project. Single modules can either be kept in RAM
 ## or stored in a rod-file.
 
-import std/[intsets, tables, hashes, strtabs, algorithm]
+import std/[intsets, tables, hashes, strtabs, algorithm, os, strutils, parseutils]
 import ../dist/checksums/src/checksums/md5
 import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages, suggestsymdb
 import ic / [packed_ast, ic]
@@ -62,8 +62,6 @@ type
     CgenPass
     EvalPass
     InterpreterPass
-    NirPass
-    NirReplPass
     GenDependPass
     Docgen2TexPass
     Docgen2JsonPass
@@ -276,6 +274,25 @@ proc someSym*(g: ModuleGraph; m: PSym; name: PIdent): PSym =
   else:
     result = strTableGet(g.ifaces[m.position].interfSelect(importHidden), name)
 
+proc someSymAmb*(g: ModuleGraph; m: PSym; name: PIdent; amb: var bool): PSym =
+  let importHidden = optImportHidden in m.options
+  if isCachedModule(g, m):
+    result = nil
+    for s in interfaceSymbols(g.config, g.cache, g.packed, FileIndex(m.position), name, importHidden):
+      if result == nil:
+        # set result to the first symbol
+        result = s
+      else:
+        # another symbol found
+        amb = true
+        break
+  else:
+    var ti: TIdentIter = default(TIdentIter)
+    result = initIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden), name)
+    if result != nil and nextIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden)) != nil:
+      # another symbol exists with same name
+      amb = true
+
 proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym =
   result = someSym(g, g.systemModule, name)
 
@@ -311,7 +328,7 @@ proc resolveInst(g: ModuleGraph; t: var LazyInstantiation): PInstantiation =
     t.inst = result
   assert result != nil
 
-proc resolveAttachedOp(g: ModuleGraph; t: var LazySym): PSym =
+proc resolveAttachedOp*(g: ModuleGraph; t: var LazySym): PSym =
   result = t.sym
   if result == nil:
     result = loadSymFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed)
@@ -454,6 +471,49 @@ proc createMagic*(g: ModuleGraph; idgen: IdGenerator; name: string, m: TMagic):
 proc createMagic(g: ModuleGraph; name: string, m: TMagic): PSym =
   result = createMagic(g, g.idgen, name, m)
 
+proc uniqueModuleName*(conf: ConfigRef; m: PSym): string =
+  ## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'}
+  ## so that it is useful as a C identifier snippet.
+  let fid = FileIndex(m.position)
+  let path = AbsoluteFile toFullPath(conf, fid)
+  var isLib = false
+  var rel = ""
+  if path.string.startsWith(conf.libpath.string):
+    isLib = true
+    rel = relativeTo(path, conf.libpath).string
+  else:
+    rel = relativeTo(path, conf.projectPath).string
+
+  if not isLib and not belongsToProjectPackage(conf, m):
+    # special handlings for nimble packages
+    when DirSep == '\\':
+      let rel2 = replace(rel, '\\', '/')
+    else:
+      let rel2 = rel
+    const pkgs2 = "pkgs2/"
+    var start = rel2.find(pkgs2)
+    if start >= 0:
+      start += pkgs2.len
+      start += skipUntil(rel2, {'/'}, start)
+      if start+1 < rel2.len:
+        rel = "pkg/" & rel2[start+1..<rel.len] # strips paths
+
+  let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len
+  result = newStringOfCap(trunc)
+  for i in 0..<trunc:
+    let c = rel[i]
+    case c
+    of 'a'..'z', '0'..'9':
+      result.add c
+    of {os.DirSep, os.AltSep}:
+      result.add 'Z' # because it looks a bit like '/'
+    of '.':
+      result.add 'O' # a circle
+    else:
+      # We mangle upper letters too so that there cannot
+      # be clashes with our special meanings of 'Z' and 'O'
+      result.addInt ord(c)
+
 proc registerModule*(g: ModuleGraph; m: PSym) =
   assert m != nil
   assert m.kind == skModule
@@ -465,7 +525,7 @@ proc registerModule*(g: ModuleGraph; m: PSym) =
     setLen(g.packed.pm, m.position + 1)
 
   g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[],
-                               uniqueName: rope(uniqueModuleName(g.config, FileIndex(m.position))))
+                               uniqueName: rope(uniqueModuleName(g.config, m)))
   initStrTables(g, m)
 
 proc registerModuleById*(g: ModuleGraph; m: FileIndex) =
@@ -617,7 +677,6 @@ proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
   if m != nil:
     g.suggestSymbols.del(fileIdx)
     g.suggestErrors.del(fileIdx)
-    g.resetForBackend
     incl m.flags, sfDirty
 
 proc unmarkAllDirty*(g: ModuleGraph) =
@@ -680,8 +739,6 @@ proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex;
 proc configComplete*(g: ModuleGraph) =
   rememberStartupConfig(g.startupPackedConfig, g.config)
 
-from std/strutils import repeat, `%`
-
 proc onProcessing*(graph: ModuleGraph, fileIdx: FileIndex, moduleStatus: string, fromModule: PSym, ) =
   let conf = graph.config
   let isNimscript = conf.isDefined("nimscript")
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 1e0a90ebd..c49ca8c9b 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -629,7 +629,7 @@ proc warningDeprecated*(conf: ConfigRef, info: TLineInfo = gCmdLineInfo, msg = "
   message(conf, info, warnDeprecated, msg)
 
 proc internalErrorImpl(conf: ConfigRef; info: TLineInfo, errMsg: string, info2: InstantiationInfo) =
-  if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
+  if conf.cmd in {cmdIdeTools, cmdCheck} and conf.structuredErrorHook.isNil: return
   writeContext(conf, info)
   liMessage(conf, info, errInternal, errMsg, doAbort, info2)
 
@@ -651,13 +651,16 @@ template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, extraM
   let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName
   liMessage(conf, info, msg, m, doNothing, instLoc())
 
-proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
-  if i.fileIndex.int32 < 0:
+proc quotedFilename*(conf: ConfigRef; fi: FileIndex): Rope =
+  if fi.int32 < 0:
     result = makeCString "???"
   elif optExcessiveStackTrace in conf.globalOptions:
-    result = conf.m.fileInfos[i.fileIndex.int32].quotedFullName
+    result = conf.m.fileInfos[fi.int32].quotedFullName
   else:
-    result = conf.m.fileInfos[i.fileIndex.int32].quotedName
+    result = conf.m.fileInfos[fi.int32].quotedName
+
+proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
+  quotedFilename(conf, i.fileIndex)
 
 template listMsg(title, r) =
   msgWriteln(conf, title, {msgNoUnitSep})
@@ -666,31 +669,6 @@ template listMsg(title, r) =
 proc listWarnings*(conf: ConfigRef) = listMsg("Warnings:", warnMin..warnMax)
 proc listHints*(conf: ConfigRef) = listMsg("Hints:", hintMin..hintMax)
 
-proc uniqueModuleName*(conf: ConfigRef; fid: FileIndex): string =
-  ## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'}
-  ## so that it is useful as a C identifier snippet.
-  let path = AbsoluteFile toFullPath(conf, fid)
-  let rel =
-    if path.string.startsWith(conf.libpath.string):
-      relativeTo(path, conf.libpath).string
-    else:
-      relativeTo(path, conf.projectPath).string
-  let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len
-  result = newStringOfCap(trunc)
-  for i in 0..<trunc:
-    let c = rel[i]
-    case c
-    of 'a'..'z':
-      result.add c
-    of {os.DirSep, os.AltSep}:
-      result.add 'Z' # because it looks a bit like '/'
-    of '.':
-      result.add 'O' # a circle
-    else:
-      # We mangle upper letters and digits too so that there cannot
-      # be clashes with our special meanings of 'Z' and 'O'
-      result.addInt ord(c)
-
 proc genSuccessX*(conf: ConfigRef) =
   let mem =
     when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem"
diff --git a/compiler/ndi.nim b/compiler/ndi.nim
index a9d9cfe79..cc18ab39f 100644
--- a/compiler/ndi.nim
+++ b/compiler/ndi.nim
@@ -29,7 +29,7 @@ proc doWrite(f: var NdiFile; s: PSym; conf: ConfigRef) =
   f.buf.add "\t"
   f.buf.addInt s.info.col.int
   f.f.write(s.name.s, "\t")
-  f.f.writeRope(s.loc.r)
+  f.f.writeRope(s.loc.snippet)
   f.f.writeLine("\t", toFullPath(conf, s.info), "\t", f.buf)
 
 template writeMangledName*(f: NdiFile; s: PSym; conf: ConfigRef) =
diff --git a/compiler/nim.nim b/compiler/nim.nim
index 3473ea443..005f11a58 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -116,9 +116,9 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
     conf.backend = backendC
 
   if conf.selectedGC == gcUnselected:
-    if conf.backend in {backendC, backendCpp, backendObjc, backendNir} or
-        (conf.cmd == cmdInteractive and isDefined(conf, "nir")) or
-        (conf.cmd in cmdDocLike and conf.backend != backendJs):
+    if conf.backend in {backendC, backendCpp, backendObjc} or
+        (conf.cmd in cmdDocLike and conf.backend != backendJs) or
+        conf.cmd == cmdGendepend:
       initOrcDefines(conf)
 
   mainCommand(graph)
diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim
deleted file mode 100644
index c8954548f..000000000
--- a/compiler/nir/ast2ir.nim
+++ /dev/null
@@ -1,2638 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2023 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-import std / [assertions, tables, sets]
-import ".." / [ast, astalgo, types, options, lineinfos, msgs, magicsys,
-  modulegraphs, renderer, transf, bitsets, trees, nimsets,
-  expanddefaults]
-from ".." / lowerings import lowerSwap, lowerTupleUnpacking
-from ".." / pathutils import customPath
-import .. / ic / bitabs
-
-import nirtypes, nirinsts, nirlineinfos, nirslots, types2ir, nirfiles
-
-when defined(nimCompilerStacktraceHints):
-  import std/stackframes
-
-type
-  ModuleCon* = ref object
-    nirm*: ref NirModule
-    types: TypesCon
-    module*: PSym
-    graph*: ModuleGraph
-    nativeIntId, nativeUIntId: TypeId
-    strPayloadId: (TypeId, TypeId)
-    idgen: IdGenerator
-    processedProcs, pendingProcsAsSet: HashSet[ItemId]
-    pendingProcs: seq[PSym] # procs we still need to generate code for
-    pendingVarsAsSet: HashSet[ItemId]
-    pendingVars: seq[PSym]
-    noModularity*: bool
-    inProc: int
-    toSymId: Table[ItemId, SymId]
-    symIdCounter: int32
-
-  ProcCon* = object
-    config*: ConfigRef
-    lit: Literals
-    lastFileKey: FileIndex
-    lastFileVal: LitId
-    labelGen: int
-    exitLabel: LabelId
-    #code*: Tree
-    blocks: seq[(PSym, LabelId)]
-    sm: SlotManager
-    idgen: IdGenerator
-    m: ModuleCon
-    prc: PSym
-    options: TOptions
-
-template code(c: ProcCon): Tree = c.m.nirm.code
-
-proc initModuleCon*(graph: ModuleGraph; config: ConfigRef; idgen: IdGenerator; module: PSym;
-                    nirm: ref NirModule): ModuleCon =
-  #let lit = Literals() # must be shared
-  result = ModuleCon(graph: graph, types: initTypesCon(config), nirm: nirm,
-    idgen: idgen, module: module)
-  case config.target.intSize
-  of 2:
-    result.nativeIntId = Int16Id
-    result.nativeUIntId = UInt16Id
-  of 4:
-    result.nativeIntId = Int32Id
-    result.nativeUIntId = UInt16Id
-  else:
-    result.nativeIntId = Int64Id
-    result.nativeUIntId = UInt16Id
-  result.strPayloadId = strPayloadPtrType(result.types, result.nirm.types)
-  nirm.namespace = nirm.lit.strings.getOrIncl(customPath(toFullPath(config, module.info)))
-  nirm.intbits = uint32(config.target.intSize * 8)
-
-proc initProcCon*(m: ModuleCon; prc: PSym; config: ConfigRef): ProcCon =
-  result = ProcCon(m: m, sm: initSlotManager({}), prc: prc, config: config,
-    lit: m.nirm.lit, idgen: m.idgen,
-    options: if prc != nil: prc.options
-             else: config.options)
-  result.exitLabel = newLabel(result.labelGen)
-
-proc toLineInfo(c: var ProcCon; i: TLineInfo): PackedLineInfo =
-  var val: LitId
-  if c.lastFileKey == i.fileIndex:
-    val = c.lastFileVal
-  else:
-    val = c.lit.strings.getOrIncl(toFullPath(c.config, i.fileIndex))
-    # remember the entry:
-    c.lastFileKey = i.fileIndex
-    c.lastFileVal = val
-  result = pack(c.m.nirm.man, val, int32 i.line, int32 i.col)
-
-proc bestEffort(c: ProcCon): TLineInfo =
-  if c.prc != nil:
-    c.prc.info
-  else:
-    c.m.module.info
-
-proc popBlock(c: var ProcCon; oldLen: int) =
-  c.blocks.setLen(oldLen)
-
-template withBlock(labl: PSym; info: PackedLineInfo; asmLabl: LabelId; body: untyped) {.dirty.} =
-  var oldLen {.gensym.} = c.blocks.len
-  c.blocks.add (labl, asmLabl)
-  body
-  popBlock(c, oldLen)
-
-type
-  GenFlag = enum
-    gfAddrOf # load the address of the expression
-    gfToOutParam # the expression is passed to an `out` parameter
-  GenFlags = set[GenFlag]
-
-proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {})
-
-proc genScope(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
-  openScope c.sm
-  gen c, n, d, flags
-  closeScope c.sm
-
-proc freeTemp(c: var ProcCon; tmp: Value) =
-  let s = extractTemp(tmp)
-  if s != SymId(-1):
-    freeTemp(c.sm, s)
-
-proc freeTemps(c: var ProcCon; tmps: openArray[Value]) =
-  for t in tmps: freeTemp(c, t)
-
-proc typeToIr(m: ModuleCon; t: PType): TypeId =
-  typeToIr(m.types, m.nirm.types, t)
-
-proc allocTemp(c: var ProcCon; t: TypeId): SymId =
-  if c.m.noModularity:
-    result = allocTemp(c.sm, t, c.m.symIdCounter)
-  else:
-    result = allocTemp(c.sm, t, c.idgen.symId)
-
-const
-  ListSymId = -1
-
-proc toSymId(c: var ProcCon; s: PSym): SymId =
-  if c.m.noModularity:
-    result = c.m.toSymId.getOrDefault(s.itemId, SymId(-1))
-    if result.int < 0:
-      inc c.m.symIdCounter
-      result = SymId(c.m.symIdCounter)
-      c.m.toSymId[s.itemId] = result
-      when ListSymId != -1:
-        if result.int == ListSymId or s.name.s == "echoBinSafe":
-          echo result.int, " is ", s.name.s, " ", c.m.graph.config $ s.info, " ", s.flags
-          writeStackTrace()
-  else:
-    result = SymId(s.itemId.item)
-
-proc getTemp(c: var ProcCon; n: PNode): Value =
-  let info = toLineInfo(c, n.info)
-  let t = typeToIr(c.m, n.typ)
-  let tmp = allocTemp(c, t)
-  c.code.addSummon info, tmp, t
-  result = localToValue(info, tmp)
-
-proc getTemp(c: var ProcCon; t: TypeId; info: PackedLineInfo): Value =
-  let tmp = allocTemp(c, t)
-  c.code.addSummon info, tmp, t
-  result = localToValue(info, tmp)
-
-proc gen(c: var ProcCon; n: PNode; flags: GenFlags = {}) =
-  var tmp = default(Value)
-  gen(c, n, tmp, flags)
-  freeTemp c, tmp
-
-proc genScope(c: var ProcCon; n: PNode; flags: GenFlags = {}) =
-  openScope c.sm
-  gen c, n, flags
-  closeScope c.sm
-
-proc genx(c: var ProcCon; n: PNode; flags: GenFlags = {}): Value =
-  result = default(Value)
-  gen(c, n, result, flags)
-  assert Tree(result).len > 0, $n
-
-proc clearDest(c: var ProcCon; n: PNode; d: var Value) {.inline.} =
-  when false:
-    if n.typ.isNil or n.typ.kind == tyVoid:
-      let s = extractTemp(d)
-      if s != SymId(-1):
-        freeLoc(c.sm, s)
-
-proc isNotOpr(n: PNode): bool =
-  n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic == mNot
-
-proc jmpBack(c: var ProcCon; n: PNode; lab: LabelId) =
-  c.code.gotoLabel toLineInfo(c, n.info), GotoLoop, lab
-
-type
-  JmpKind = enum opcFJmp, opcTJmp
-
-proc xjmp(c: var ProcCon; n: PNode; jk: JmpKind; v: Value): LabelId =
-  result = newLabel(c.labelGen)
-  let info = toLineInfo(c, n.info)
-  buildTyped c.code, info, Select, Bool8Id:
-    c.code.copyTree Tree(v)
-    build c.code, info, SelectPair:
-      build c.code, info, SelectValue:
-        c.code.boolVal(c.lit.numbers, info, jk == opcTJmp)
-      c.code.gotoLabel info, Goto, result
-
-proc patch(c: var ProcCon; n: PNode; L: LabelId) =
-  addLabel c.code, toLineInfo(c, n.info), Label, L
-
-proc genWhile(c: var ProcCon; n: PNode) =
-  # lab1:
-  #   cond, tmp
-  #   fjmp tmp, lab2
-  #   body
-  #   jmp lab1
-  # lab2:
-  let info = toLineInfo(c, n.info)
-  let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel)
-  withBlock(nil, info, lab1):
-    if isTrue(n[0]):
-      c.gen(n[1])
-      c.jmpBack(n, lab1)
-    elif isNotOpr(n[0]):
-      var tmp = c.genx(n[0][1])
-      let lab2 = c.xjmp(n, opcTJmp, tmp)
-      c.freeTemp(tmp)
-      c.gen(n[1])
-      c.jmpBack(n, lab1)
-      c.patch(n, lab2)
-    else:
-      var tmp = c.genx(n[0])
-      let lab2 = c.xjmp(n, opcFJmp, tmp)
-      c.freeTemp(tmp)
-      c.gen(n[1])
-      c.jmpBack(n, lab1)
-      c.patch(n, lab2)
-
-proc genBlock(c: var ProcCon; n: PNode; d: var Value) =
-  openScope c.sm
-  let info = toLineInfo(c, n.info)
-  let lab1 = newLabel(c.labelGen)
-
-  withBlock(n[0].sym, info, lab1):
-    c.gen(n[1], d)
-
-  c.code.addLabel(info, Label, lab1)
-  closeScope c.sm
-  c.clearDest(n, d)
-
-proc jumpTo(c: var ProcCon; n: PNode; L: LabelId) =
-  c.code.addLabel(toLineInfo(c, n.info), Goto, L)
-
-proc genBreak(c: var ProcCon; n: PNode) =
-  if n[0].kind == nkSym:
-    for i in countdown(c.blocks.len-1, 0):
-      if c.blocks[i][0] == n[0].sym:
-        c.jumpTo n, c.blocks[i][1]
-        return
-    localError(c.config, n.info, "NIR problem: cannot find 'break' target")
-  else:
-    c.jumpTo n, c.blocks[c.blocks.high][1]
-
-proc genIf(c: var ProcCon; n: PNode; d: var Value) =
-  #  if (!expr1) goto lab1;
-  #    thenPart
-  #    goto LEnd
-  #  lab1:
-  #  if (!expr2) goto lab2;
-  #    thenPart2
-  #    goto LEnd
-  #  lab2:
-  #    elsePart
-  #  Lend:
-  if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n)
-  var ending = newLabel(c.labelGen)
-  for i in 0..<n.len:
-    var it = n[i]
-    if it.len == 2:
-      let info = toLineInfo(c, it[0].info)
-      var elsePos: LabelId
-      if isNotOpr(it[0]):
-        let tmp = c.genx(it[0][1])
-        elsePos = c.xjmp(it[0][1], opcTJmp, tmp) # if true
-        c.freeTemp tmp
-      else:
-        let tmp = c.genx(it[0])
-        elsePos = c.xjmp(it[0], opcFJmp, tmp) # if false
-        c.freeTemp tmp
-      c.clearDest(n, d)
-      if isEmptyType(it[1].typ): # maybe noreturn call, don't touch `d`
-        c.genScope(it[1])
-      else:
-        c.genScope(it[1], d) # then part
-      if i < n.len-1:
-        c.jumpTo it[1], ending
-      c.patch(it, elsePos)
-    else:
-      c.clearDest(n, d)
-      if isEmptyType(it[0].typ): # maybe noreturn call, don't touch `d`
-        c.genScope(it[0])
-      else:
-        c.genScope(it[0], d)
-  c.patch(n, ending)
-  c.clearDest(n, d)
-
-proc tempToDest(c: var ProcCon; n: PNode; d: var Value; tmp: Value) =
-  if isEmpty(d):
-    d = tmp
-  else:
-    let info = toLineInfo(c, n.info)
-    buildTyped c.code, info, Asgn, typeToIr(c.m, n.typ):
-      c.code.copyTree d
-      c.code.copyTree tmp
-    freeTemp(c, tmp)
-
-proc genAndOr(c: var ProcCon; n: PNode; opc: JmpKind; d: var Value) =
-  #   asgn d, a
-  #   tjmp|fjmp lab1
-  #   asgn d, b
-  # lab1:
-  var tmp = getTemp(c, n)
-  c.gen(n[1], tmp)
-  let lab1 = c.xjmp(n, opc, tmp)
-  c.gen(n[2], tmp)
-  c.patch(n, lab1)
-  tempToDest c, n, d, tmp
-
-proc unused(c: var ProcCon; n: PNode; x: Value) {.inline.} =
-  if hasValue(x):
-    #debug(n)
-    localError(c.config, n.info, "not unused")
-
-proc caseValue(c: var ProcCon; n: PNode) =
-  let info = toLineInfo(c, n.info)
-  build c.code, info, SelectValue:
-    let x = genx(c, n)
-    c.code.copyTree x
-    freeTemp(c, x)
-
-proc caseRange(c: var ProcCon; n: PNode) =
-  let info = toLineInfo(c, n.info)
-  build c.code, info, SelectRange:
-    let x = genx(c, n[0])
-    let y = genx(c, n[1])
-    c.code.copyTree x
-    c.code.copyTree y
-    freeTemp(c, y)
-    freeTemp(c, x)
-
-proc addUseCodegenProc(c: var ProcCon; dest: var Tree; name: string; info: PackedLineInfo) =
-  let cp = getCompilerProc(c.m.graph, name)
-  let theProc = c.genx newSymNode(cp)
-  copyTree c.code, theProc
-
-template buildCond(useNegation: bool; cond: typed; body: untyped) =
-  let lab = newLabel(c.labelGen)
-  buildTyped c.code, info, Select, Bool8Id:
-    c.code.copyTree cond
-    build c.code, info, SelectPair:
-      build c.code, info, SelectValue:
-        c.code.boolVal(c.lit.numbers, info, useNegation)
-      c.code.gotoLabel info, Goto, lab
-
-  body
-  c.code.addLabel info, Label, lab
-
-template buildIf(cond: typed; body: untyped) =
-  buildCond false, cond, body
-
-template buildIfNot(cond: typed; body: untyped) =
-  buildCond true, cond, body
-
-template buildIfThenElse(cond: typed; then, otherwise: untyped) =
-  let lelse = newLabel(c.labelGen)
-  let lend = newLabel(c.labelGen)
-  buildTyped c.code, info, Select, Bool8Id:
-    c.code.copyTree cond
-    build c.code, info, SelectPair:
-      build c.code, info, SelectValue:
-        c.code.boolVal(c.lit.numbers, info, false)
-      c.code.gotoLabel info, Goto, lelse
-
-  then()
-  c.code.gotoLabel info, Goto, lend
-  c.code.addLabel info, Label, lelse
-  otherwise()
-  c.code.addLabel info, Label, lend
-
-include stringcases
-
-proc genCase(c: var ProcCon; n: PNode; d: var Value) =
-  if not isEmptyType(n.typ):
-    if isEmpty(d): d = getTemp(c, n)
-  else:
-    unused(c, n, d)
-
-  if n[0].typ.skipTypes(abstractInst).kind == tyString:
-    genStringCase(c, n, d)
-    return
-
-  var sections = newSeqOfCap[LabelId](n.len-1)
-  let ending = newLabel(c.labelGen)
-  let info = toLineInfo(c, n.info)
-  let tmp = c.genx(n[0])
-  buildTyped c.code, info, Select, typeToIr(c.m, n[0].typ):
-    c.code.copyTree tmp
-    for i in 1..<n.len:
-      let section = newLabel(c.labelGen)
-      sections.add section
-      let it = n[i]
-      let itinfo = toLineInfo(c, it.info)
-      build c.code, itinfo, SelectPair:
-        build c.code, itinfo, SelectList:
-          for j in 0..<it.len-1:
-            if it[j].kind == nkRange:
-              caseRange c, it[j]
-            else:
-              caseValue c, it[j]
-        c.code.addLabel itinfo, Goto, section
-  c.freeTemp tmp
-  for i in 1..<n.len:
-    let it = n[i]
-    let itinfo = toLineInfo(c, it.info)
-    c.code.addLabel itinfo, Label, sections[i-1]
-    c.gen it.lastSon
-    if i != n.len-1:
-      c.code.addLabel itinfo, Goto, ending
-  c.code.addLabel info, Label, ending
-
-proc rawCall(c: var ProcCon; info: PackedLineInfo; opc: Opcode; t: TypeId; args: var openArray[Value]) =
-  buildTyped c.code, info, opc, t:
-    if opc in {CheckedCall, CheckedIndirectCall}:
-      c.code.addLabel info, CheckedGoto, c.exitLabel
-    for a in mitems(args):
-      c.code.copyTree a
-      freeTemp c, a
-
-proc canRaiseDisp(c: ProcCon; n: PNode): bool =
-  # we assume things like sysFatal cannot raise themselves
-  if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}:
-    result = false
-  elif optPanics in c.config.globalOptions or
-      (n.kind == nkSym and sfSystemModule in getModule(n.sym).flags and
-       sfSystemRaisesDefect notin n.sym.flags):
-    # we know we can be strict:
-    result = canRaise(n)
-  else:
-    # we have to be *very* conservative:
-    result = canRaiseConservative(n)
-
-proc genCall(c: var ProcCon; n: PNode; d: var Value) =
-  let canRaise = canRaiseDisp(c, n[0])
-
-  let opc = if n[0].kind == nkSym and n[0].sym.kind in routineKinds:
-              (if canRaise: CheckedCall else: Call)
-            else:
-              (if canRaise: CheckedIndirectCall else: IndirectCall)
-  let info = toLineInfo(c, n.info)
-
-  # In the IR we cannot nest calls. Thus we use two passes:
-  var args: seq[Value] = @[]
-  var t = n[0].typ
-  if t != nil: t = t.skipTypes(abstractInst)
-  args.add genx(c, n[0])
-  for i in 1..<n.len:
-    if t != nil and i < t.len:
-      if isCompileTimeOnly(t[i]): discard
-      elif isOutParam(t[i]): args.add genx(c, n[i], {gfToOutParam})
-      else: args.add genx(c, n[i])
-    else:
-      args.add genx(c, n[i])
-
-  let tb = typeToIr(c.m, n.typ)
-  if not isEmptyType(n.typ):
-    if isEmpty(d): d = getTemp(c, n)
-    # XXX Handle problematic aliasing here: `a = f_canRaise(a)`.
-    buildTyped c.code, info, Asgn, tb:
-      c.code.copyTree d
-      rawCall c, info, opc, tb, args
-  else:
-    rawCall c, info, opc, tb, args
-  freeTemps c, args
-
-proc genRaise(c: var ProcCon; n: PNode) =
-  let info = toLineInfo(c, n.info)
-  let tb = typeToIr(c.m, n[0].typ)
-
-  let d = genx(c, n[0])
-  buildTyped c.code, info, SetExc, tb:
-    c.code.copyTree d
-  c.freeTemp(d)
-  c.code.addLabel info, Goto, c.exitLabel
-
-proc genReturn(c: var ProcCon; n: PNode) =
-  if n[0].kind != nkEmpty:
-    gen(c, n[0])
-  # XXX Block leave actions?
-  let info = toLineInfo(c, n.info)
-  c.code.addLabel info, Goto, c.exitLabel
-
-proc genTry(c: var ProcCon; n: PNode; d: var Value) =
-  if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n)
-  var endings: seq[LabelId] = @[]
-  let ehPos = newLabel(c.labelGen)
-  let oldExitLab = c.exitLabel
-  c.exitLabel = ehPos
-  if isEmptyType(n[0].typ): # maybe noreturn call, don't touch `d`
-    c.gen(n[0])
-  else:
-    c.gen(n[0], d)
-  c.clearDest(n, d)
-
-  # Add a jump past the exception handling code
-  let jumpToFinally = newLabel(c.labelGen)
-  c.jumpTo n, jumpToFinally
-  # This signals where the body ends and where the exception handling begins
-  c.patch(n, ehPos)
-  c.exitLabel = oldExitLab
-  for i in 1..<n.len:
-    let it = n[i]
-    if it.kind != nkFinally:
-      # first opcExcept contains the end label of the 'except' block:
-      let endExcept = newLabel(c.labelGen)
-      for j in 0..<it.len - 1:
-        assert(it[j].kind == nkType)
-        let typ = it[j].typ.skipTypes(abstractPtrs-{tyTypeDesc})
-        let itinfo = toLineInfo(c, it[j].info)
-        build c.code, itinfo, TestExc:
-          c.code.addTyped itinfo, typeToIr(c.m, typ)
-      if it.len == 1:
-        let itinfo = toLineInfo(c, it.info)
-        build c.code, itinfo, TestExc:
-          c.code.addTyped itinfo, VoidId
-      let body = it.lastSon
-      if isEmptyType(body.typ): # maybe noreturn call, don't touch `d`
-        c.gen(body)
-      else:
-        c.gen(body, d)
-      c.clearDest(n, d)
-      if i < n.len:
-        endings.add newLabel(c.labelGen)
-      c.patch(it, endExcept)
-  let fin = lastSon(n)
-  # we always generate an 'opcFinally' as that pops the safepoint
-  # from the stack if no exception is raised in the body.
-  c.patch(fin, jumpToFinally)
-  #c.gABx(fin, opcFinally, 0, 0)
-  for endPos in endings: c.patch(n, endPos)
-  if fin.kind == nkFinally:
-    c.gen(fin[0])
-    c.clearDest(n, d)
-  #c.gABx(fin, opcFinallyEnd, 0, 0)
-
-template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar
-proc isGlobal(n: PNode): bool = n.kind == nkSym and isGlobal(n.sym)
-
-proc genField(c: var ProcCon; n: PNode; d: var Value) =
-  var pos: int
-  if n.kind != nkSym or n.sym.kind != skField:
-    localError(c.config, n.info, "no field symbol")
-    pos = 0
-  else:
-    pos = n.sym.position
-  d.addImmediateVal toLineInfo(c, n.info), pos
-
-proc genIndex(c: var ProcCon; n: PNode; arr: PType; d: var Value) =
-  let info = toLineInfo(c, n.info)
-  if arr.skipTypes(abstractInst).kind == tyArray and
-      (let offset = firstOrd(c.config, arr); offset != Zero):
-    let x = c.genx(n)
-    buildTyped d, info, Sub, c.m.nativeIntId:
-      copyTree d.Tree, x
-      d.addImmediateVal toLineInfo(c, n.info), toInt(offset)
-  else:
-    c.gen(n, d)
-  if optBoundsCheck in c.options:
-    let idx = move d
-    build d, info, CheckedIndex:
-      d.Tree.addLabel info, CheckedGoto, c.exitLabel
-      copyTree d.Tree, idx
-      let x = toInt64 lengthOrd(c.config, arr)
-      d.addIntVal c.lit.numbers, info, c.m.nativeIntId, x
-
-proc rawGenNew(c: var ProcCon; d: Value; refType: PType; ninfo: TLineInfo; needsInit: bool) =
-  assert refType.kind == tyRef
-  let baseType = refType.elementType
-
-  let info = toLineInfo(c, ninfo)
-  let codegenProc = magicsys.getCompilerProc(c.m.graph,
-    if needsInit: "nimNewObj" else: "nimNewObjUninit")
-  let refTypeIr = typeToIr(c.m, refType)
-  buildTyped c.code, info, Asgn, refTypeIr:
-    copyTree c.code, d
-    buildTyped c.code, info, Cast, refTypeIr:
-      buildTyped c.code, info, Call, VoidPtrId:
-        let theProc = c.genx newSymNode(codegenProc, ninfo)
-        copyTree c.code, theProc
-        c.code.addImmediateVal info, int(getSize(c.config, baseType))
-        c.code.addImmediateVal info, int(getAlign(c.config, baseType))
-
-proc genNew(c: var ProcCon; n: PNode; needsInit: bool) =
-  # If in doubt, always follow the blueprint of the C code generator for `mm:orc`.
-  let refType = n[1].typ.skipTypes(abstractInstOwned)
-  let d = genx(c, n[1])
-  rawGenNew c, d, refType, n.info, needsInit
-  freeTemp c, d
-
-proc genNewSeqOfCap(c: var ProcCon; n: PNode; d: var Value) =
-  let info = toLineInfo(c, n.info)
-  let seqtype = skipTypes(n.typ, abstractVarRange)
-  let baseType = seqtype.elementType
-  var a = c.genx(n[1])
-  if isEmpty(d): d = getTemp(c, n)
-  # $1.len = 0
-  buildTyped c.code, info, Asgn, c.m.nativeIntId:
-    buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype):
-      copyTree c.code, d
-      c.code.addImmediateVal info, 0
-    c.code.addImmediateVal info, 0
-  # $1.p = ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3))
-  let payloadPtr = seqPayloadPtrType(c.m.types, c.m.nirm.types, seqtype)[0]
-  buildTyped c.code, info, Asgn, payloadPtr:
-    # $1.p
-    buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype):
-      copyTree c.code, d
-      c.code.addImmediateVal info, 1
-    # ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3))
-    buildTyped c.code, info, Cast, payloadPtr:
-      buildTyped c.code, info, Call, VoidPtrId:
-        let codegenProc = magicsys.getCompilerProc(c.m.graph, "newSeqPayloadUninit")
-        let theProc = c.genx newSymNode(codegenProc, n.info)
-        copyTree c.code, theProc
-        copyTree c.code, a
-        c.code.addImmediateVal info, int(getSize(c.config, baseType))
-        c.code.addImmediateVal info, int(getAlign(c.config, baseType))
-  freeTemp c, a
-
-proc genNewSeqPayload(c: var ProcCon; info: PackedLineInfo; d, b: Value; seqtype: PType) =
-  let baseType = seqtype.elementType
-  # $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3))
-  let payloadPtr = seqPayloadPtrType(c.m.types, c.m.nirm.types, seqtype)[0]
-
-  # $1.len = $2
-  buildTyped c.code, info, Asgn, c.m.nativeIntId:
-    buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype):
-      copyTree c.code, d
-      c.code.addImmediateVal info, 0
-    copyTree c.code, b
-
-  buildTyped c.code, info, Asgn, payloadPtr:
-    # $1.p
-    buildTyped c.code, info, FieldAt, typeToIr(c.m, seqtype):
-      copyTree c.code, d
-      c.code.addImmediateVal info, 1
-    # ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3))
-    buildTyped c.code, info, Cast, payloadPtr:
-      buildTyped c.code, info, Call, VoidPtrId:
-        let codegenProc = magicsys.getCompilerProc(c.m.graph, "newSeqPayload")
-        let theProc = c.genx newSymNode(codegenProc)
-        copyTree c.code, theProc
-        copyTree c.code, b
-        c.code.addImmediateVal info, int(getSize(c.config, baseType))
-        c.code.addImmediateVal info, int(getAlign(c.config, baseType))
-
-proc genNewSeq(c: var ProcCon; n: PNode) =
-  let info = toLineInfo(c, n.info)
-  let seqtype = skipTypes(n[1].typ, abstractVarRange)
-  var d = c.genx(n[1])
-  var b = c.genx(n[2])
-
-  genNewSeqPayload(c, info, d, b, seqtype)
-
-  freeTemp c, b
-  freeTemp c, d
-
-template intoDest*(d: var Value; info: PackedLineInfo; typ: TypeId; body: untyped) =
-  if typ == VoidId:
-    body(c.code)
-  elif isEmpty(d):
-    body(Tree(d))
-  else:
-    buildTyped c.code, info, Asgn, typ:
-      copyTree c.code, d
-      body(c.code)
-
-template valueIntoDest(c: var ProcCon; info: PackedLineInfo; d: var Value; typ: PType; body: untyped) =
-  if isEmpty(d):
-    body(Tree d)
-  else:
-    buildTyped c.code, info, Asgn, typeToIr(c.m, typ):
-      copyTree c.code, d
-      body(c.code)
-
-template constrIntoDest(c: var ProcCon; info: PackedLineInfo; d: var Value; typ: PType; body: untyped) =
-  var tmp = default(Value)
-  body(Tree tmp)
-  if isEmpty(d):
-    d = tmp
-  else:
-    buildTyped c.code, info, Asgn, typeToIr(c.m, typ):
-      copyTree c.code, d
-      copyTree c.code, tmp
-
-proc genBinaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) =
-  let info = toLineInfo(c, n.info)
-  let tmp = c.genx(n[1])
-  let tmp2 = c.genx(n[2])
-  let t = typeToIr(c.m, n.typ)
-  template body(target) =
-    buildTyped target, info, opc, t:
-      if opc in {CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedMod}:
-        target.addLabel info, CheckedGoto, c.exitLabel
-      copyTree target, tmp
-      copyTree target, tmp2
-  intoDest d, info, t, body
-  c.freeTemp(tmp)
-  c.freeTemp(tmp2)
-
-proc genCmpOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) =
-  let info = toLineInfo(c, n.info)
-  let tmp = c.genx(n[1])
-  let tmp2 = c.genx(n[2])
-  let t = typeToIr(c.m, n[1].typ)
-  template body(target) =
-    buildTyped target, info, opc, t:
-      copyTree target, tmp
-      copyTree target, tmp2
-  intoDest d, info, Bool8Id, body
-  c.freeTemp(tmp)
-  c.freeTemp(tmp2)
-
-proc genUnaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) =
-  let info = toLineInfo(c, n.info)
-  let tmp = c.genx(n[1])
-  let t = typeToIr(c.m, n.typ)
-  template body(target) =
-    buildTyped target, info, opc, t:
-      copyTree target, tmp
-  intoDest d, info, t, body
-  c.freeTemp(tmp)
-
-proc genIncDec(c: var ProcCon; n: PNode; opc: Opcode) =
-  let info = toLineInfo(c, n.info)
-  let t = typeToIr(c.m, skipTypes(n[1].typ, abstractVar))
-
-  let d = c.genx(n[1])
-  let tmp = c.genx(n[2])
-  # we produce code like:  i = i + 1
-  buildTyped c.code, info, Asgn, t:
-    copyTree c.code, d
-    buildTyped c.code, info, opc, t:
-      if opc in {CheckedAdd, CheckedSub}:
-        c.code.addLabel info, CheckedGoto, c.exitLabel
-      copyTree c.code, d
-      copyTree c.code, tmp
-  c.freeTemp(tmp)
-  #c.genNarrow(n[1], d)
-  c.freeTemp(d)
-
-proc genArrayLen(c: var ProcCon; n: PNode; d: var Value) =
-  #echo c.m.graph.config $ n.info, " ", n
-  let info = toLineInfo(c, n.info)
-  var a = n[1]
-  #if a.kind == nkHiddenAddr: a = a[0]
-  var typ = skipTypes(a.typ, abstractVar + tyUserTypeClasses)
-  case typ.kind
-  of tyOpenArray, tyVarargs:
-    let xa = c.genx(a)
-    template body(target) =
-      buildTyped target, info, FieldAt, typeToIr(c.m, typ):
-        copyTree target, xa
-        target.addImmediateVal info, 1 # (p, len)-pair so len is at index 1
-    intoDest d, info, c.m.nativeIntId, body
-
-  of tyCstring:
-    let xa = c.genx(a)
-    if isEmpty(d): d = getTemp(c, n)
-    buildTyped c.code, info, Call, c.m.nativeIntId:
-      let codegenProc = magicsys.getCompilerProc(c.m.graph, "nimCStrLen")
-      assert codegenProc != nil
-      let theProc = c.genx newSymNode(codegenProc, n.info)
-      copyTree c.code, theProc
-      copyTree c.code, xa
-
-  of tyString, tySequence:
-    let xa = c.genx(a)
-
-    if typ.kind == tySequence:
-      # we go through a temporary here because people write bullshit code.
-      if isEmpty(d): d = getTemp(c, n)
-
-    template body(target) =
-      buildTyped target, info, FieldAt, typeToIr(c.m, typ):
-        copyTree target, xa
-        target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0
-    intoDest d, info, c.m.nativeIntId, body
-
-  of tyArray:
-    template body(target) =
-      target.addIntVal(c.lit.numbers, info, c.m.nativeIntId, toInt lengthOrd(c.config, typ))
-    intoDest d, info, c.m.nativeIntId, body
-  else: internalError(c.config, n.info, "genArrayLen()")
-
-proc genUnaryMinus(c: var ProcCon; n: PNode; d: var Value) =
-  let info = toLineInfo(c, n.info)
-  let tmp = c.genx(n[1])
-  let t = typeToIr(c.m, n.typ)
-  template body(target) =
-    buildTyped target, info, Sub, t:
-      # Little hack: This works because we know that `0.0` is all 0 bits:
-      target.addIntVal(c.lit.numbers, info, t, 0)
-      copyTree target, tmp
-  intoDest d, info, t, body
-  c.freeTemp(tmp)
-
-proc genHigh(c: var ProcCon; n: PNode; d: var Value) =
-  let info = toLineInfo(c, n.info)
-  let t = typeToIr(c.m, n.typ)
-  var x = default(Value)
-  genArrayLen(c, n, x)
-  template body(target) =
-    buildTyped target, info, Sub, t:
-      copyTree target, x
-      target.addIntVal(c.lit.numbers, info, t, 1)
-  intoDest d, info, t, body
-  c.freeTemp x
-
-proc genBinaryCp(c: var ProcCon; n: PNode; d: var Value; compilerProc: string) =
-  let info = toLineInfo(c, n.info)
-  let xa = c.genx(n[1])
-  let xb = c.genx(n[2])
-  if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n)
-
-  let t = typeToIr(c.m, n.typ)
-  template body(target) =
-    buildTyped target, info, Call, t:
-      let codegenProc = magicsys.getCompilerProc(c.m.graph, compilerProc)
-      #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info)
-      let theProc = c.genx newSymNode(codegenProc, n.info)
-      copyTree target, theProc
-      copyTree target, xa
-      copyTree target, xb
-
-  intoDest d, info, t, body
-  c.freeTemp xb
-  c.freeTemp xa
-
-proc genUnaryCp(c: var ProcCon; n: PNode; d: var Value; compilerProc: string; argAt = 1) =
-  let info = toLineInfo(c, n.info)
-  let xa = c.genx(n[argAt])
-  if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n)
-
-  let t = typeToIr(c.m, n.typ)
-  template body(target) =
-    buildTyped target, info, Call, t:
-      let codegenProc = magicsys.getCompilerProc(c.m.graph, compilerProc)
-      let theProc = c.genx newSymNode(codegenProc, n.info)
-      copyTree target, theProc
-      copyTree target, xa
-
-  intoDest d, info, t, body
-  c.freeTemp xa
-
-proc genEnumToStr(c: var ProcCon; n: PNode; d: var Value) =
-  let t = n[1].typ.skipTypes(abstractInst+{tyRange})
-  let toStrProc = getToStringProc(c.m.graph, t)
-  # XXX need to modify this logic for IC.
-  var nb = copyTree(n)
-  nb[0] = newSymNode(toStrProc)
-  gen(c, nb, d)
-
-proc genOf(c: var ProcCon; n: PNode; d: var Value) =
-  genUnaryOp c, n, d, TestOf
-
-template sizeOfLikeMsg(name): string =
-  "'" & name & "' requires '.importc' types to be '.completeStruct'"
-
-proc genIsNil(c: var ProcCon; n: PNode; d: var Value) =
-  let info = toLineInfo(c, n.info)
-  let tmp = c.genx(n[1])
-  let t = typeToIr(c.m, n[1].typ)
-  template body(target) =
-    buildTyped target, info, Eq, t:
-      copyTree target, tmp
-      addNilVal target, info, t
-  intoDest d, info, Bool8Id, body
-  c.freeTemp(tmp)
-
-proc fewCmps(conf: ConfigRef; s: PNode): bool =
-  # this function estimates whether it is better to emit code
-  # for constructing the set or generating a bunch of comparisons directly
-  if s.kind != nkCurly:
-    result = false
-  elif (getSize(conf, s.typ) <= conf.target.intSize) and (nfAllConst in s.flags):
-    result = false            # it is better to emit the set generation code
-  elif elemType(s.typ).kind in {tyInt, tyInt16..tyInt64}:
-    result = true             # better not emit the set if int is basetype!
-  else:
-    result = s.len <= 8  # 8 seems to be a good value
-
-proc genInBitset(c: var ProcCon; n: PNode; d: var Value) =
-  let info = toLineInfo(c, n.info)
-  let a = c.genx(n[1])
-  let b = c.genx(n[2])
-
-  let t = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ)
-  let setType = typeToIr(c.m, n[1].typ)
-  let mask =
-    case t
-    of UInt8Id: 7
-    of UInt16Id: 15
-    of UInt32Id: 31
-    else: 63
-  let expansion = if t == UInt64Id: UInt64Id else: c.m.nativeUIntId
-    # "(($1              &(1U<<((NU)($2)&7U)))!=0)"  - or -
-    # "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)"
-
-  template body(target) =
-    buildTyped target, info, BoolNot, Bool8Id:
-      buildTyped target, info, Eq, t:
-        buildTyped target, info, BitAnd, t:
-          if c.m.nirm.types[setType].kind != ArrayTy:
-            copyTree target, a
-          else:
-            buildTyped target, info, ArrayAt, setType:
-              copyTree target, a
-              buildTyped target, info, BitShr, t:
-                buildTyped target, info, Cast, expansion:
-                  copyTree target, b
-                addIntVal target, c.lit.numbers, info, expansion, 3
-
-          buildTyped target, info, BitShl, t:
-            addIntVal target, c.lit.numbers, info, t, 1
-            buildTyped target, info, BitAnd, t:
-              buildTyped target, info, Cast, expansion:
-                copyTree target, b
-              addIntVal target, c.lit.numbers, info, expansion, mask
-        addIntVal target, c.lit.numbers, info, t, 0
-  intoDest d, info, t, body
-
-  c.freeTemp(b)
-  c.freeTemp(a)
-
-proc genInSet(c: var ProcCon; n: PNode; d: var Value) =
-  let g {.cursor.} = c.m.graph
-  if n[1].kind == nkCurly and fewCmps(g.config, n[1]):
-    # a set constructor but not a constant set:
-    # do not emit the set, but generate a bunch of comparisons; and if we do
-    # so, we skip the unnecessary range check: This is a semantical extension
-    # that code now relies on. :-/ XXX
-    let elem = if n[2].kind in {nkChckRange, nkChckRange64}: n[2][0]
-               else: n[2]
-    let curly = n[1]
-    var ex: PNode = nil
-    for it in curly:
-      var test: PNode
-      if it.kind == nkRange:
-        test = newTree(nkCall, g.operators.opAnd.newSymNode,
-          newTree(nkCall, g.operators.opLe.newSymNode, it[0], elem), # a <= elem
-          newTree(nkCall, g.operators.opLe.newSymNode, elem, it[1])
-        )
-      else:
-        test = newTree(nkCall, g.operators.opEq.newSymNode, elem, it)
-      test.typ = getSysType(g, it.info, tyBool)
-
-      if ex == nil: ex = test
-      else: ex = newTree(nkCall, g.operators.opOr.newSymNode, ex, test)
-
-    if ex == nil:
-      let info = toLineInfo(c, n.info)
-      template body(target) =
-        boolVal target, c.lit.numbers, info, false
-      intoDest d, info, Bool8Id, body
-    else:
-      gen c, ex, d
-  else:
-    genInBitset c, n, d
-
-proc genCard(c: var ProcCon; n: PNode; d: var Value) =
-  let info = toLineInfo(c, n.info)
-  let a = c.genx(n[1])
-  let t = typeToIr(c.m, n.typ)
-
-  let setType = typeToIr(c.m, n[1].typ)
-  if isEmpty(d): d = getTemp(c, n)
-
-  buildTyped c.code, info, Asgn, t:
-    copyTree c.code, d
-    buildTyped c.code, info, Call, t:
-      if c.m.nirm.types[setType].kind == ArrayTy:
-        let codegenProc = magicsys.getCompilerProc(c.m.graph, "cardSet")
-        let theProc = c.genx newSymNode(codegenProc, n.info)
-        copyTree c.code, theProc
-        buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, setType):
-          copyTree c.code, a
-        c.code.addImmediateVal info, int(getSize(c.config, n[1].typ))
-      elif t == UInt64Id:
-        let codegenProc = magicsys.getCompilerProc(c.m.graph, "countBits64")
-        let theProc = c.genx newSymNode(codegenProc, n.info)
-        copyTree c.code, theProc
-        copyTree c.code, a
-      else:
-        let codegenProc = magicsys.getCompilerProc(c.m.graph, "countBits32")
-        let theProc = c.genx newSymNode(codegenProc, n.info)
-        copyTree c.code, theProc
-        buildTyped c.code, info, Cast, UInt32Id:
-          copyTree c.code, a
-  freeTemp c, a
-
-proc genEqSet(c: var ProcCon; n: PNode; d: var Value) =
-  let info = toLineInfo(c, n.info)
-  let a = c.genx(n[1])
-  let b = c.genx(n[2])
-  let t = typeToIr(c.m, n.typ)
-
-  let setType = typeToIr(c.m, n[1].typ)
-
-  if c.m.nirm.types[setType].kind == ArrayTy:
-    if isEmpty(d): d = getTemp(c, n)
-
-    buildTyped c.code, info, Asgn, t:
-      copyTree c.code, d
-      buildTyped c.code, info, Eq, t:
-        buildTyped c.code, info, Call, t:
-          let codegenProc = magicsys.getCompilerProc(c.m.graph, "nimCmpMem")
-          let theProc = c.genx newSymNode(codegenProc, n.info)
-          copyTree c.code, theProc
-          buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, setType):
-            copyTree c.code, a
-          buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, setType):
-            copyTree c.code, b
-          c.code.addImmediateVal info, int(getSize(c.config, n[1].typ))
-        c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
-
-  else:
-    template body(target) =
-      buildTyped target, info, Eq, setType:
-        copyTree target, a
-        copyTree target, b
-    intoDest d, info, Bool8Id, body
-
-  freeTemp c, b
-  freeTemp c, a
-
-proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: int): (SymId, LabelId, LabelId) =
-  let tmp = allocTemp(c, c.m.nativeIntId)
-  c.code.addSummon info, tmp, c.m.nativeIntId
-  buildTyped c.code, info, Asgn, c.m.nativeIntId:
-    c.code.addSymUse info, tmp
-    c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, first
-  let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel)
-  result = (tmp, lab1, newLabel(c.labelGen))
-
-  buildTyped c.code, info, Select, Bool8Id:
-    buildTyped c.code, info, Lt, c.m.nativeIntId:
-      c.code.addSymUse info, tmp
-      c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, last
-    build c.code, info, SelectPair:
-      build c.code, info, SelectValue:
-        c.code.boolVal(c.lit.numbers, info, false)
-      c.code.gotoLabel info, Goto, result[2]
-
-proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: Value): (SymId, LabelId, LabelId) =
-  let tmp = allocTemp(c, c.m.nativeIntId)
-  c.code.addSummon info, tmp, c.m.nativeIntId
-  buildTyped c.code, info, Asgn, c.m.nativeIntId:
-    c.code.addSymUse info, tmp
-    copyTree c.code, first
-  let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel)
-  result = (tmp, lab1, newLabel(c.labelGen))
-
-  buildTyped c.code, info, Select, Bool8Id:
-    buildTyped c.code, info, Le, c.m.nativeIntId:
-      c.code.addSymUse info, tmp
-      copyTree c.code, last
-    build c.code, info, SelectPair:
-      build c.code, info, SelectValue:
-        c.code.boolVal(c.lit.numbers, info, false)
-      c.code.gotoLabel info, Goto, result[2]
-
-proc endLoop(c: var ProcCon; info: PackedLineInfo; s: SymId; back, exit: LabelId) =
-  buildTyped c.code, info, Asgn, c.m.nativeIntId:
-    c.code.addSymUse info, s
-    buildTyped c.code, info, Add, c.m.nativeIntId:
-      c.code.addSymUse info, s
-      c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1
-  c.code.addLabel info, GotoLoop, back
-  c.code.addLabel info, Label, exit
-  freeTemp(c.sm, s)
-
-proc genLeSet(c: var ProcCon; n: PNode; d: var Value) =
-  let info = toLineInfo(c, n.info)
-  let a = c.genx(n[1])
-  let b = c.genx(n[2])
-  let t = typeToIr(c.m, n.typ)
-
-  let setType = typeToIr(c.m, n[1].typ)
-
-  if c.m.nirm.types[setType].kind == ArrayTy:
-    let elemType = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ)
-    if isEmpty(d): d = getTemp(c, n)
-    #    "for ($1 = 0; $1 < $2; $1++):"
-    #    "  $3 = (($4[$1] & ~ $5[$1]) == 0)"
-    #    "  if (!$3) break;"
-    let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, int(getSize(c.config, n[1].typ)))
-    buildTyped c.code, info, Asgn, Bool8Id:
-      copyTree c.code, d
-      buildTyped c.code, info, Eq, elemType:
-        buildTyped c.code, info, BitAnd, elemType:
-          buildTyped c.code, info, ArrayAt, setType:
-            copyTree c.code, a
-            c.code.addSymUse info, idx
-          buildTyped c.code, info, BitNot, elemType:
-            buildTyped c.code, info, ArrayAt, setType:
-              copyTree c.code, b
-              c.code.addSymUse info, idx
-        c.code.addIntVal c.lit.numbers, info, elemType, 0
-
-    # if !$3: break
-    buildTyped c.code, info, Select, Bool8Id:
-      c.code.copyTree d
-      build c.code, info, SelectPair:
-        build c.code, info, SelectValue:
-          c.code.boolVal(c.lit.numbers, info, false)
-        c.code.gotoLabel info, Goto, endLabel
-
-    endLoop(c, info, idx, backLabel, endLabel)
-  else:
-    # "(($1 & ~ $2)==0)"
-    template body(target) =
-      buildTyped target, info, Eq, setType:
-        buildTyped target, info, BitAnd, setType:
-          copyTree target, a
-          buildTyped target, info, BitNot, setType:
-            copyTree target, b
-        target.addIntVal c.lit.numbers, info, setType, 0
-
-    intoDest d, info, Bool8Id, body
-
-  freeTemp c, b
-  freeTemp c, a
-
-proc genLtSet(c: var ProcCon; n: PNode; d: var Value) =
-  localError(c.m.graph.config, n.info, "`<` for sets not implemented")
-
-proc genBinarySet(c: var ProcCon; n: PNode; d: var Value; m: TMagic) =
-  let info = toLineInfo(c, n.info)
-  let a = c.genx(n[1])
-  let b = c.genx(n[2])
-  let t = typeToIr(c.m, n.typ)
-
-  let setType = typeToIr(c.m, n[1].typ)
-
-  if c.m.nirm.types[setType].kind == ArrayTy:
-    let elemType = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ)
-    if isEmpty(d): d = getTemp(c, n)
-    #    "for ($1 = 0; $1 < $2; $1++):"
-    #    "  $3 = (($4[$1] & ~ $5[$1]) == 0)"
-    #    "  if (!$3) break;"
-    let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, int(getSize(c.config, n[1].typ)))
-    buildTyped c.code, info, Asgn, elemType:
-      buildTyped c.code, info, ArrayAt, setType:
-        copyTree c.code, d
-        c.code.addSymUse info, idx
-      buildTyped c.code, info, (if m == mPlusSet: BitOr else: BitAnd), elemType:
-        buildTyped c.code, info, ArrayAt, setType:
-          copyTree c.code, a
-          c.code.addSymUse info, idx
-        if m == mMinusSet:
-          buildTyped c.code, info, BitNot, elemType:
-            buildTyped c.code, info, ArrayAt, setType:
-              copyTree c.code, b
-              c.code.addSymUse info, idx
-        else:
-          buildTyped c.code, info, ArrayAt, setType:
-            copyTree c.code, b
-            c.code.addSymUse info, idx
-
-    endLoop(c, info, idx, backLabel, endLabel)
-  else:
-    # "(($1 & ~ $2)==0)"
-    template body(target) =
-      buildTyped target, info, (if m == mPlusSet: BitOr else: BitAnd), setType:
-        copyTree target, a
-        if m == mMinusSet:
-          buildTyped target, info, BitNot, setType:
-            copyTree target, b
-        else:
-          copyTree target, b
-
-    intoDest d, info, setType, body
-
-  freeTemp c, b
-  freeTemp c, a
-
-proc genInclExcl(c: var ProcCon; n: PNode; m: TMagic) =
-  let info = toLineInfo(c, n.info)
-  let a = c.genx(n[1])
-  let b = c.genx(n[2])
-
-  let setType = typeToIr(c.m, n[1].typ)
-
-  let t = bitsetBasetype(c.m.types, c.m.nirm.types, n[1].typ)
-  let mask =
-    case t
-    of UInt8Id: 7
-    of UInt16Id: 15
-    of UInt32Id: 31
-    else: 63
-
-  buildTyped c.code, info, Asgn, setType:
-    if c.m.nirm.types[setType].kind == ArrayTy:
-      if m == mIncl:
-        # $1[(NU)($2)>>3] |=(1U<<($2&7U))
-        buildTyped c.code, info, ArrayAt, setType:
-          copyTree c.code, a
-          buildTyped c.code, info, BitShr, t:
-            buildTyped c.code, info, Cast, c.m.nativeUIntId:
-              copyTree c.code, b
-            addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
-        buildTyped c.code, info, BitOr, t:
-          buildTyped c.code, info, ArrayAt, setType:
-            copyTree c.code, a
-            buildTyped c.code, info, BitShr, t:
-              buildTyped c.code, info, Cast, c.m.nativeUIntId:
-                copyTree c.code, b
-              addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
-          buildTyped c.code, info, BitShl, t:
-            c.code.addIntVal c.lit.numbers, info, t, 1
-            buildTyped c.code, info, BitAnd, t:
-              copyTree c.code, b
-              c.code.addIntVal c.lit.numbers, info, t, 7
-      else:
-        # $1[(NU)($2)>>3] &= ~(1U<<($2&7U))
-        buildTyped c.code, info, ArrayAt, setType:
-          copyTree c.code, a
-          buildTyped c.code, info, BitShr, t:
-            buildTyped c.code, info, Cast, c.m.nativeUIntId:
-              copyTree c.code, b
-            addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
-        buildTyped c.code, info, BitAnd, t:
-          buildTyped c.code, info, ArrayAt, setType:
-            copyTree c.code, a
-            buildTyped c.code, info, BitShr, t:
-              buildTyped c.code, info, Cast, c.m.nativeUIntId:
-                copyTree c.code, b
-              addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
-          buildTyped c.code, info, BitNot, t:
-            buildTyped c.code, info, BitShl, t:
-              c.code.addIntVal c.lit.numbers, info, t, 1
-              buildTyped c.code, info, BitAnd, t:
-                copyTree c.code, b
-                c.code.addIntVal c.lit.numbers, info, t, 7
-
-    else:
-      copyTree c.code, a
-      if m == mIncl:
-        # $1 |= ((NU8)1)<<(($2) & 7)
-        buildTyped c.code, info, BitOr, setType:
-          copyTree c.code, a
-          buildTyped c.code, info, BitShl, t:
-            c.code.addIntVal c.lit.numbers, info, t, 1
-            buildTyped c.code, info, BitAnd, t:
-              copyTree c.code, b
-              c.code.addIntVal c.lit.numbers, info, t, mask
-      else:
-        # $1 &= ~(((NU8)1) << (($2) & 7))
-        buildTyped c.code, info, BitAnd, setType:
-          copyTree c.code, a
-          buildTyped c.code, info, BitNot, t:
-            buildTyped c.code, info, BitShl, t:
-              c.code.addIntVal c.lit.numbers, info, t, 1
-              buildTyped c.code, info, BitAnd, t:
-                copyTree c.code, b
-                c.code.addIntVal c.lit.numbers, info, t, mask
-  freeTemp c, b
-  freeTemp c, a
-
-proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) =
-  # example: { a..b, c, d, e, f..g }
-  # we have to emit an expression of the form:
-  # nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c);
-  # incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g);
-  let info = toLineInfo(c, n.info)
-  let setType = typeToIr(c.m, n.typ)
-  let size = int(getSize(c.config, n.typ))
-  let t = bitsetBasetype(c.m.types, c.m.nirm.types, n.typ)
-  let mask =
-    case t
-    of UInt8Id: 7
-    of UInt16Id: 15
-    of UInt32Id: 31
-    else: 63
-
-  if isEmpty(d): d = getTemp(c, n)
-  if c.m.nirm.types[setType].kind != ArrayTy:
-    buildTyped c.code, info, Asgn, setType:
-      copyTree c.code, d
-      c.code.addIntVal c.lit.numbers, info, t, 0
-
-    for it in n:
-      if it.kind == nkRange:
-        let a = genx(c, it[0])
-        let b = genx(c, it[1])
-        let (idx, backLabel, endLabel) = beginCountLoop(c, info, a, b)
-        buildTyped c.code, info, Asgn, setType:
-          copyTree c.code, d
-          buildTyped c.code, info, BitAnd, setType:
-            copyTree c.code, d
-            buildTyped c.code, info, BitNot, t:
-              buildTyped c.code, info, BitShl, t:
-                c.code.addIntVal c.lit.numbers, info, t, 1
-                buildTyped c.code, info, BitAnd, t:
-                  c.code.addSymUse info, idx
-                  c.code.addIntVal c.lit.numbers, info, t, mask
-
-        endLoop(c, info, idx, backLabel, endLabel)
-        freeTemp c, b
-        freeTemp c, a
-
-      else:
-        let a = genx(c, it)
-        buildTyped c.code, info, Asgn, setType:
-          copyTree c.code, d
-          buildTyped c.code, info, BitAnd, setType:
-            copyTree c.code, d
-            buildTyped c.code, info, BitNot, t:
-              buildTyped c.code, info, BitShl, t:
-                c.code.addIntVal c.lit.numbers, info, t, 1
-                buildTyped c.code, info, BitAnd, t:
-                  copyTree c.code, a
-                  c.code.addIntVal c.lit.numbers, info, t, mask
-        freeTemp c, a
-
-  else:
-    # init loop:
-    let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, size)
-    buildTyped c.code, info, Asgn, t:
-      copyTree c.code, d
-      c.code.addIntVal c.lit.numbers, info, t, 0
-    endLoop(c, info, idx, backLabel, endLabel)
-
-    # incl elements:
-    for it in n:
-      if it.kind == nkRange:
-        let a = genx(c, it[0])
-        let b = genx(c, it[1])
-        let (idx, backLabel, endLabel) = beginCountLoop(c, info, a, b)
-
-        buildTyped c.code, info, Asgn, t:
-          buildTyped c.code, info, ArrayAt, setType:
-            copyTree c.code, d
-            buildTyped c.code, info, BitShr, t:
-              buildTyped c.code, info, Cast, c.m.nativeUIntId:
-                c.code.addSymUse info, idx
-              addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
-          buildTyped c.code, info, BitOr, t:
-            buildTyped c.code, info, ArrayAt, setType:
-              copyTree c.code, d
-              buildTyped c.code, info, BitShr, t:
-                buildTyped c.code, info, Cast, c.m.nativeUIntId:
-                  c.code.addSymUse info, idx
-                addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
-            buildTyped c.code, info, BitShl, t:
-              c.code.addIntVal c.lit.numbers, info, t, 1
-              buildTyped c.code, info, BitAnd, t:
-                c.code.addSymUse info, idx
-                c.code.addIntVal c.lit.numbers, info, t, 7
-
-        endLoop(c, info, idx, backLabel, endLabel)
-        freeTemp c, b
-        freeTemp c, a
-
-      else:
-        let a = genx(c, it)
-        # $1[(NU)($2)>>3] |=(1U<<($2&7U))
-        buildTyped c.code, info, Asgn, t:
-          buildTyped c.code, info, ArrayAt, setType:
-            copyTree c.code, d
-            buildTyped c.code, info, BitShr, t:
-              buildTyped c.code, info, Cast, c.m.nativeUIntId:
-                copyTree c.code, a
-              addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
-          buildTyped c.code, info, BitOr, t:
-            buildTyped c.code, info, ArrayAt, setType:
-              copyTree c.code, d
-              buildTyped c.code, info, BitShr, t:
-                buildTyped c.code, info, Cast, c.m.nativeUIntId:
-                  copyTree c.code, a
-                addIntVal c.code, c.lit.numbers, info, c.m.nativeUIntId, 3
-            buildTyped c.code, info, BitShl, t:
-              c.code.addIntVal c.lit.numbers, info, t, 1
-              buildTyped c.code, info, BitAnd, t:
-                copyTree c.code, a
-                c.code.addIntVal c.lit.numbers, info, t, 7
-        freeTemp c, a
-
-proc genSetConstr(c: var ProcCon; n: PNode; d: var Value) =
-  if isDeepConstExpr(n):
-    let info = toLineInfo(c, n.info)
-    let setType = typeToIr(c.m, n.typ)
-    let size = int(getSize(c.config, n.typ))
-    let cs = toBitSet(c.config, n)
-
-    if c.m.nirm.types[setType].kind != ArrayTy:
-      template body(target) =
-        target.addIntVal c.lit.numbers, info, setType, cast[BiggestInt](bitSetToWord(cs, size))
-      intoDest d, info, setType, body
-    else:
-      let t = bitsetBasetype(c.m.types, c.m.nirm.types, n.typ)
-      template body(target) =
-        buildTyped target, info, ArrayConstr, setType:
-          for i in 0..high(cs):
-            target.addIntVal c.lit.numbers, info, t, int64 cs[i]
-      intoDest d, info, setType, body
-  else:
-    genSetConstrDyn c, n, d
-
-proc genStrConcat(c: var ProcCon; n: PNode; d: var Value) =
-  let info = toLineInfo(c, n.info)
-  #   <Nim code>
-  #   s = "Hello " & name & ", how do you feel?" & 'z'
-  #
-  #   <generated code>
-  #  {
-  #    string tmp0;
-  #    ...
-  #    tmp0 = rawNewString(6 + 17 + 1 + s2->len);
-  #    // we cannot generate s = rawNewString(...) here, because
-  #    // ``s`` may be used on the right side of the expression
-  #    appendString(tmp0, strlit_1);
-  #    appendString(tmp0, name);
-  #    appendString(tmp0, strlit_2);
-  #    appendChar(tmp0, 'z');
-  #    asgn(s, tmp0);
-  #  }
-  var args: seq[Value] = @[]
-  var argsRuntimeLen: seq[Value] = @[]
-
-  var precomputedLen = 0
-  for i in 1 ..< n.len:
-    let it = n[i]
-    args.add genx(c, it)
-    if skipTypes(it.typ, abstractVarRange).kind == tyChar:
-      inc precomputedLen
-    elif it.kind in {nkStrLit..nkTripleStrLit}:
-      inc precomputedLen, it.strVal.len
-    else:
-      argsRuntimeLen.add args[^1]
-
-  # generate length computation:
-  var tmpLen = allocTemp(c, c.m.nativeIntId)
-  buildTyped c.code, info, Asgn, c.m.nativeIntId:
-    c.code.addSymUse info, tmpLen
-    c.code.addIntVal c.lit.numbers, info, c.m.nativeIntId, precomputedLen
-  for a in mitems(argsRuntimeLen):
-    buildTyped c.code, info, Asgn, c.m.nativeIntId:
-      c.code.addSymUse info, tmpLen
-      buildTyped c.code, info, CheckedAdd, c.m.nativeIntId:
-        c.code.addLabel info, CheckedGoto, c.exitLabel
-        c.code.addSymUse info, tmpLen
-        buildTyped c.code, info, FieldAt, typeToIr(c.m, n.typ):
-          copyTree c.code, a
-          c.code.addImmediateVal info, 0 # (len, p)-pair so len is at index 0
-
-  var tmpStr = getTemp(c, n)
-  #    ^ because of aliasing, we always go through a temporary
-  let t = typeToIr(c.m, n.typ)
-  buildTyped c.code, info, Asgn, t:
-    copyTree c.code, tmpStr
-    buildTyped c.code, info, Call, t:
-      let codegenProc = magicsys.getCompilerProc(c.m.graph, "rawNewString")
-      #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info)
-      let theProc = c.genx newSymNode(codegenProc, n.info)
-      copyTree c.code, theProc
-      c.code.addSymUse info, tmpLen
-  freeTemp c.sm, tmpLen
-
-  for i in 1 ..< n.len:
-    let it = n[i]
-    let isChar = skipTypes(it.typ, abstractVarRange).kind == tyChar
-    buildTyped c.code, info, Call, VoidId:
-      let codegenProc = magicsys.getCompilerProc(c.m.graph,
-        (if isChar: "appendChar" else: "appendString"))
-      #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info)
-      let theProc = c.genx newSymNode(codegenProc, n.info)
-      copyTree c.code, theProc
-      buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, t):
-        copyTree c.code, tmpStr
-      copyTree c.code, args[i-1]
-    freeTemp c, args[i-1]
-
-  if isEmpty(d):
-    d = tmpStr
-  else:
-    # XXX Test that this does not cause memory leaks!
-    buildTyped c.code, info, Asgn, t:
-      copyTree c.code, d
-      copyTree c.code, tmpStr
-
-proc genDefault(c: var ProcCon; n: PNode; d: var Value) =
-  let m = expandDefault(n.typ, n.info)
-  gen c, m, d
-
-proc genWasMoved(c: var ProcCon; n: PNode) =
-  let n1 = n[1].skipAddr
-  # XXX We need a way to replicate this logic or better yet a better
-  # solution for injectdestructors.nim:
-  #if c.withinBlockLeaveActions > 0 and notYetAlive(n1):
-  var d = c.genx(n1)
-  assert not isEmpty(d)
-  let m = expandDefault(n1.typ, n1.info)
-  gen c, m, d
-
-proc genMove(c: var ProcCon; n: PNode; d: var Value) =
-  let info = toLineInfo(c, n.info)
-  let n1 = n[1].skipAddr
-  var a = c.genx(n1)
-  if n.len == 4:
-    # generated by liftdestructors:
-    let src = c.genx(n[2])
-    # if ($1.p == $2.p) goto lab1
-    let lab1 = newLabel(c.labelGen)
-
-    let n1t = typeToIr(c.m, n1.typ)
-    let payloadType = seqPayloadPtrType(c.m.types, c.m.nirm.types, n1.typ)[0]
-    buildTyped c.code, info, Select, Bool8Id:
-      buildTyped c.code, info, Eq, payloadType:
-        buildTyped c.code, info, FieldAt, n1t:
-          copyTree c.code, a
-          c.code.addImmediateVal info, 1 # (len, p)-pair
-        buildTyped c.code, info, FieldAt, n1t:
-          copyTree c.code, src
-          c.code.addImmediateVal info, 1 # (len, p)-pair
-
-      build c.code, info, SelectPair:
-        build c.code, info, SelectValue:
-          c.code.boolVal(c.lit.numbers, info, true)
-        c.code.gotoLabel info, Goto, lab1
-
-    gen(c, n[3])
-    c.patch n, lab1
-
-    buildTyped c.code, info, Asgn, typeToIr(c.m, n1.typ):
-      copyTree c.code, a
-      copyTree c.code, src
-
-  else:
-    if isEmpty(d): d = getTemp(c, n)
-    buildTyped c.code, info, Asgn, typeToIr(c.m, n1.typ):
-      copyTree c.code, d
-      copyTree c.code, a
-    var op = getAttachedOp(c.m.graph, n.typ, attachedWasMoved)
-    if op == nil or skipTypes(n1.typ, abstractVar+{tyStatic}).kind in {tyOpenArray, tyVarargs}:
-      let m = expandDefault(n1.typ, n1.info)
-      gen c, m, a
-    else:
-      var opB = c.genx(newSymNode(op))
-      buildTyped c.code, info, Call, typeToIr(c.m, n.typ):
-        copyTree c.code, opB
-        buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.nirm.types, typeToIr(c.m, n1.typ)):
-          copyTree c.code, a
-
-template fieldAt(x: Value; i: int; t: TypeId): Tree =
-  var result = default(Tree)
-  buildTyped result, info, FieldAt, t:
-    copyTree result, x
-    result.addImmediateVal info, i
-  result
-
-template eqNil(x: Tree; t: TypeId): Tree =
-  var result = default(Tree)
-  buildTyped result, info, Eq, t:
-    copyTree result, x
-    result.addNilVal info, t
-  result
-
-template eqZero(x: Tree): Tree =
-  var result = default(Tree)
-  buildTyped result, info, Eq, c.m.nativeIntId:
-    copyTree result, x
-    result.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
-  result
-
-template bitOp(x: Tree; opc: Opcode; y: int): Tree =
-  var result = default(Tree)
-  buildTyped result, info, opc, c.m.nativeIntId:
-    copyTree result, x
-    result.addIntVal c.lit.numbers, info, c.m.nativeIntId, y
-  result
-
-proc genDestroySeq(c: var ProcCon; n: PNode; t: PType) =
-  let info = toLineInfo(c, n.info)
-  let strLitFlag = 1 shl (c.m.graph.config.target.intSize * 8 - 2) # see also NIM_STRLIT_FLAG
-
-  let x = c.genx(n[1])
-  let baseType = t.elementType
-
-  let seqType = typeToIr(c.m, t)
-  let p = fieldAt(x, 0, seqType)
-
-  # if $1.p != nil and ($1.p.cap and NIM_STRLIT_FLAG) == 0:
-  #   alignedDealloc($1.p, NIM_ALIGNOF($2))
-  buildIfNot p.eqNil(seqType):
-    buildIf fieldAt(Value(p), 0, seqPayloadPtrType(c.m.types, c.m.nirm.types, t)[0]).bitOp(BitAnd, 0).eqZero():
-      let codegenProc = getCompilerProc(c.m.graph, "alignedDealloc")
-      buildTyped c.code, info, Call, VoidId:
-        let theProc = c.genx newSymNode(codegenProc, n.info)
-        copyTree c.code, theProc
-        copyTree c.code, p
-        c.code.addImmediateVal info, int(getAlign(c.config, baseType))
-
-  freeTemp c, x
-
-proc genDestroy(c: var ProcCon; n: PNode) =
-  let t = n[1].typ.skipTypes(abstractInst)
-  case t.kind
-  of tyString:
-    var unused = default(Value)
-    genUnaryCp(c, n, unused, "nimDestroyStrV1")
-  of tySequence:
-    genDestroySeq(c, n, t)
-  else: discard "nothing to do"
-
-type
-  IndexFor = enum
-    ForSeq, ForStr, ForOpenArray, ForArray
-
-proc genIndexCheck(c: var ProcCon; n: PNode; a: Value; kind: IndexFor; arr: PType): Value =
-  if optBoundsCheck in c.options:
-    let info = toLineInfo(c, n.info)
-    result = default(Value)
-    let idx = genx(c, n)
-    build result, info, CheckedIndex:
-      result.Tree.addLabel info, CheckedGoto, c.exitLabel
-      copyTree result.Tree, idx
-      case kind
-      of ForSeq, ForStr:
-        buildTyped result, info, FieldAt, typeToIr(c.m, arr):
-          copyTree result.Tree, a
-          result.addImmediateVal info, 0 # (len, p)-pair
-      of ForOpenArray:
-        buildTyped result, info, FieldAt, typeToIr(c.m, arr):
-          copyTree result.Tree, a
-          result.addImmediateVal info, 1 # (p, len)-pair
-      of ForArray:
-        let x = toInt64 lengthOrd(c.config, arr)
-        result.addIntVal c.lit.numbers, info, c.m.nativeIntId, x
-    freeTemp c, idx
-  else:
-    result = genx(c, n)
-
-proc addSliceFields(c: var ProcCon; target: var Tree; info: PackedLineInfo;
-                    x: Value; n: PNode; arrType: PType) =
-  let elemType = arrayPtrTypeOf(c.m.nirm.types, typeToIr(c.m, arrType.elementType))
-  case arrType.kind
-  of tyString, tySequence:
-    let checkKind = if arrType.kind == tyString: ForStr else: ForSeq
-    let pay = if checkKind == ForStr: c.m.strPayloadId
-              else: seqPayloadPtrType(c.m.types, c.m.nirm.types, arrType)
-
-    let y = genIndexCheck(c, n[2], x, checkKind, arrType)
-    let z = genIndexCheck(c, n[3], x, checkKind, arrType)
-
-    buildTyped target, info, ObjConstr, typeToIr(c.m, n.typ):
-      target.addImmediateVal info, 0
-      buildTyped target, info, AddrOf, elemType:
-        buildTyped target, info, DerefArrayAt, pay[1]:
-          buildTyped target, info, FieldAt, typeToIr(c.m, arrType):
-            copyTree target, x
-            target.addImmediateVal info, 1 # (len, p)-pair
-          copyTree target, y
-
-      # len:
-      target.addImmediateVal info, 1
-      buildTyped target, info, Add, c.m.nativeIntId:
-        buildTyped target, info, Sub, c.m.nativeIntId:
-          copyTree target, z
-          copyTree target, y
-        target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1
-
-    freeTemp c, z
-    freeTemp c, y
-  of tyArray:
-    # XXX This evaluates the index check for `y` twice.
-    # This check is also still insufficient for non-zero based arrays.
-    let y = genIndexCheck(c, n[2], x, ForArray, arrType)
-    let z = genIndexCheck(c, n[3], x, ForArray, arrType)
-
-    buildTyped target, info, ObjConstr, typeToIr(c.m, n.typ):
-      target.addImmediateVal info, 0
-      buildTyped target, info, AddrOf, elemType:
-        buildTyped target, info, ArrayAt, typeToIr(c.m, arrType):
-          copyTree target, x
-          copyTree target, y
-
-      target.addImmediateVal info, 1
-      buildTyped target, info, Add, c.m.nativeIntId:
-        buildTyped target, info, Sub, c.m.nativeIntId:
-          copyTree target, z
-          copyTree target, y
-        target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1
-
-    freeTemp c, z
-    freeTemp c, y
-  of tyOpenArray:
-    # XXX This evaluates the index check for `y` twice.
-    let y = genIndexCheck(c, n[2], x, ForOpenArray, arrType)
-    let z = genIndexCheck(c, n[3], x, ForOpenArray, arrType)
-    let pay = openArrayPayloadType(c.m.types, c.m.nirm.types, arrType)
-
-    buildTyped target, info, ObjConstr, typeToIr(c.m, n.typ):
-      target.addImmediateVal info, 0
-      buildTyped target, info, AddrOf, elemType:
-        buildTyped target, info, DerefArrayAt, pay:
-          buildTyped target, info, FieldAt, typeToIr(c.m, arrType):
-            copyTree target, x
-            target.addImmediateVal info, 0 # (p, len)-pair
-          copyTree target, y
-
-      target.addImmediateVal info, 1
-      buildTyped target, info, Add, c.m.nativeIntId:
-        buildTyped target, info, Sub, c.m.nativeIntId:
-          copyTree target, z
-          copyTree target, y
-        target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 1
-
-    freeTemp c, z
-    freeTemp c, y
-  else:
-    raiseAssert "addSliceFields: " & typeToString(arrType)
-
-proc genSlice(c: var ProcCon; n: PNode; d: var Value) =
-  let info = toLineInfo(c, n.info)
-
-  let x = c.genx(n[1])
-
-  let arrType = n[1].typ.skipTypes(abstractVar)
-
-  template body(target) =
-    c.addSliceFields target, info, x, n, arrType
-
-  valueIntoDest c, info, d, arrType, body
-  freeTemp c, x
-
-proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) =
-  case m
-  of mAnd: c.genAndOr(n, opcFJmp, d)
-  of mOr: c.genAndOr(n, opcTJmp, d)
-  of mPred, mSubI: c.genBinaryOp(n, d, if optOverflowCheck in c.options: CheckedSub else: Sub)
-  of mSucc, mAddI: c.genBinaryOp(n, d, if optOverflowCheck in c.options: CheckedAdd else: Add)
-  of mInc:
-    unused(c, n, d)
-    c.genIncDec(n, if optOverflowCheck in c.options: CheckedAdd else: Add)
-  of mDec:
-    unused(c, n, d)
-    c.genIncDec(n, if optOverflowCheck in c.options: CheckedSub else: Sub)
-  of mOrd, mChr, mUnown:
-    c.gen(n[1], d)
-  of generatedMagics:
-    genCall(c, n, d)
-  of mNew, mNewFinalize:
-    unused(c, n, d)
-    c.genNew(n, needsInit = true)
-  of mNewSeq:
-    unused(c, n, d)
-    c.genNewSeq(n)
-  of mNewSeqOfCap: c.genNewSeqOfCap(n, d)
-  of mNewString, mNewStringOfCap, mExit: c.genCall(n, d)
-  of mLengthOpenArray, mLengthArray, mLengthSeq, mLengthStr:
-    genArrayLen(c, n, d)
-  of mMulI: genBinaryOp(c, n, d, if optOverflowCheck in c.options: CheckedMul else: Mul)
-  of mDivI: genBinaryOp(c, n, d, if optOverflowCheck in c.options: CheckedDiv else: Div)
-  of mModI: genBinaryOp(c, n, d, if optOverflowCheck in c.options: CheckedMod else: Mod)
-  of mAddF64: genBinaryOp(c, n, d, Add)
-  of mSubF64: genBinaryOp(c, n, d, Sub)
-  of mMulF64: genBinaryOp(c, n, d, Mul)
-  of mDivF64: genBinaryOp(c, n, d, Div)
-  of mShrI: genBinaryOp(c, n, d, BitShr)
-  of mShlI: genBinaryOp(c, n, d, BitShl)
-  of mAshrI: genBinaryOp(c, n, d, BitShr)
-  of mBitandI: genBinaryOp(c, n, d, BitAnd)
-  of mBitorI: genBinaryOp(c, n, d, BitOr)
-  of mBitxorI: genBinaryOp(c, n, d, BitXor)
-  of mAddU: genBinaryOp(c, n, d, Add)
-  of mSubU: genBinaryOp(c, n, d, Sub)
-  of mMulU: genBinaryOp(c, n, d, Mul)
-  of mDivU: genBinaryOp(c, n, d, Div)
-  of mModU: genBinaryOp(c, n, d, Mod)
-  of mEqI, mEqB, mEqEnum, mEqCh:
-    genCmpOp(c, n, d, Eq)
-  of mLeI, mLeEnum, mLeCh, mLeB:
-    genCmpOp(c, n, d, Le)
-  of mLtI, mLtEnum, mLtCh, mLtB:
-    genCmpOp(c, n, d, Lt)
-  of mEqF64: genCmpOp(c, n, d, Eq)
-  of mLeF64: genCmpOp(c, n, d, Le)
-  of mLtF64: genCmpOp(c, n, d, Lt)
-  of mLePtr, mLeU: genCmpOp(c, n, d, Le)
-  of mLtPtr, mLtU: genCmpOp(c, n, d, Lt)
-  of mEqProc, mEqRef:
-    genCmpOp(c, n, d, Eq)
-  of mXor: genBinaryOp(c, n, d, BitXor)
-  of mNot: genUnaryOp(c, n, d, BoolNot)
-  of mUnaryMinusI, mUnaryMinusI64:
-    genUnaryMinus(c, n, d)
-    #genNarrow(c, n, d)
-  of mUnaryMinusF64: genUnaryMinus(c, n, d)
-  of mUnaryPlusI, mUnaryPlusF64: gen(c, n[1], d)
-  of mBitnotI:
-    genUnaryOp(c, n, d, BitNot)
-    when false:
-      # XXX genNarrowU modified, do not narrow signed types
-      let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
-      let size = getSize(c.config, t)
-      if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8):
-        c.gABC(n, opcNarrowU, d, TRegister(size*8))
-  of mStrToStr, mEnsureMove: c.gen n[1], d
-  of mBoolToStr: genUnaryCp(c, n, d, "nimBoolToStr")
-  of mCharToStr: genUnaryCp(c, n, d, "nimCharToStr")
-  of mCStrToStr: genUnaryCp(c, n, d, "cstrToNimstr")
-  of mEnumToStr: genEnumToStr(c, n, d)
-
-  of mEqStr: genBinaryCp(c, n, d, "eqStrings")
-  of mEqCString: genCall(c, n, d)
-  of mLeStr: genBinaryCp(c, n, d, "leStrings")
-  of mLtStr: genBinaryCp(c, n, d, "ltStrings")
-
-  of mSetLengthStr:
-    unused(c, n, d)
-    let nb = copyTree(n)
-    nb[1] = makeAddr(nb[1], c.m.idgen)
-    genBinaryCp(c, nb, d, "setLengthStrV2")
-
-  of mSetLengthSeq:
-    unused(c, n, d)
-    let nb = copyTree(n)
-    nb[1] = makeAddr(nb[1], c.m.idgen)
-    genCall(c, nb, d)
-
-  of mSwap:
-    unused(c, n, d)
-    c.gen(lowerSwap(c.m.graph, n, c.m.idgen,
-      if c.prc == nil: c.m.module else: c.prc), d)
-  of mParseBiggestFloat:
-    genCall c, n, d
-  of mHigh:
-    c.genHigh n, d
-
-  of mEcho:
-    unused(c, n, d)
-    genUnaryCp c, n, d, "echoBinSafe"
-
-  of mAppendStrCh:
-    unused(c, n, d)
-    let nb = copyTree(n)
-    nb[1] = makeAddr(nb[1], c.m.idgen)
-    genBinaryCp(c, nb, d, "nimAddCharV1")
-  of mMinI, mMaxI, mAbsI, mDotDot:
-    c.genCall(n, d)
-  of mSizeOf:
-    localError(c.config, n.info, sizeOfLikeMsg("sizeof"))
-  of mAlignOf:
-    localError(c.config, n.info, sizeOfLikeMsg("alignof"))
-  of mOffsetOf:
-    localError(c.config, n.info, sizeOfLikeMsg("offsetof"))
-  of mRunnableExamples:
-    discard "just ignore any call to runnableExamples"
-  of mOf: genOf(c, n, d)
-  of mAppendStrStr:
-    unused(c, n, d)
-    let nb = copyTree(n)
-    nb[1] = makeAddr(nb[1], c.m.idgen)
-    genBinaryCp(c, nb, d, "nimAddStrV1")
-  of mAppendSeqElem:
-    unused(c, n, d)
-    let nb = copyTree(n)
-    nb[1] = makeAddr(nb[1], c.m.idgen)
-    genCall(c, nb, d)
-  of mIsNil: genIsNil(c, n, d)
-  of mInSet: genInSet(c, n, d)
-  of mCard: genCard(c, n, d)
-  of mEqSet: genEqSet(c, n, d)
-  of mLeSet: genLeSet(c, n, d)
-  of mLtSet: genLtSet(c, n, d)
-  of mMulSet: genBinarySet(c, n, d, m)
-  of mPlusSet: genBinarySet(c, n, d, m)
-  of mMinusSet: genBinarySet(c, n, d, m)
-  of mIncl, mExcl:
-    unused(c, n, d)
-    genInclExcl(c, n, m)
-  of mConStrStr: genStrConcat(c, n, d)
-  of mDefault, mZeroDefault:
-    genDefault c, n, d
-  of mMove: genMove(c, n, d)
-  of mWasMoved, mReset:
-    unused(c, n, d)
-    genWasMoved(c, n)
-  of mDestroy: genDestroy(c, n)
-  #of mAccessEnv: unaryExpr(d, n, d, "$1.ClE_0")
-  #of mAccessTypeField: genAccessTypeField(c, n, d)
-  of mSlice: genSlice(c, n, d)
-  of mTrace: discard "no code to generate"
-  else:
-    # mGCref, mGCunref: unused by ORC
-    globalError(c.config, n.info, "cannot generate code for: " & $m)
-
-proc canElimAddr(n: PNode; idgen: IdGenerator): PNode =
-  result = nil
-  case n[0].kind
-  of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
-    var m = n[0][0]
-    if m.kind in {nkDerefExpr, nkHiddenDeref}:
-      # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
-      result = copyNode(n[0])
-      result.add m[0]
-      if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
-        result.typ = n.typ
-      elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
-        result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen)
-  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
-    var m = n[0][1]
-    if m.kind in {nkDerefExpr, nkHiddenDeref}:
-      # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
-      result = copyNode(n[0])
-      result.add n[0][0]
-      result.add m[0]
-      if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
-        result.typ = n.typ
-      elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
-        result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen)
-  else:
-    if n[0].kind in {nkDerefExpr, nkHiddenDeref}:
-      # addr ( deref ( x )) --> x
-      result = n[0][0]
-
-proc genAddr(c: var ProcCon; n: PNode; d: var Value, flags: GenFlags) =
-  if (let m = canElimAddr(n, c.m.idgen); m != nil):
-    gen(c, m, d, flags)
-    return
-
-  let info = toLineInfo(c, n.info)
-  let tmp = c.genx(n[0], flags)
-  template body(target) =
-    buildTyped target, info, AddrOf, typeToIr(c.m, n.typ):
-      copyTree target, tmp
-
-  valueIntoDest c, info, d, n.typ, body
-  freeTemp c, tmp
-
-proc genDeref(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
-  let info = toLineInfo(c, n.info)
-  let tmp = c.genx(n[0], flags)
-  template body(target) =
-    buildTyped target, info, Load, typeToIr(c.m, n.typ):
-      copyTree target, tmp
-
-  valueIntoDest c, info, d, n.typ, body
-  freeTemp c, tmp
-
-proc addAddrOfFirstElem(c: var ProcCon; target: var Tree; info: PackedLineInfo; tmp: Value; typ: PType) =
-  let arrType = typ.skipTypes(abstractVar)
-  let elemType = arrayPtrTypeOf(c.m.nirm.types, typeToIr(c.m, arrType.elementType))
-  case arrType.kind
-  of tyString:
-    let t = typeToIr(c.m, typ)
-    target.addImmediateVal info, 0
-    buildTyped target, info, AddrOf, elemType:
-      buildTyped target, info, DerefArrayAt, c.m.strPayloadId[1]:
-        buildTyped target, info, FieldAt, typeToIr(c.m, arrType):
-          copyTree target, tmp
-          target.addImmediateVal info, 1 # (len, p)-pair
-        target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
-    # len:
-    target.addImmediateVal info, 1
-    buildTyped target, info, FieldAt, typeToIr(c.m, arrType):
-      copyTree target, tmp
-      target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0
-
-  of tySequence:
-    let t = typeToIr(c.m, typ)
-    target.addImmediateVal info, 0
-    buildTyped target, info, AddrOf, elemType:
-      buildTyped target, info, DerefArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, typ)[1]:
-        buildTyped target, info, FieldAt, typeToIr(c.m, arrType):
-          copyTree target, tmp
-          target.addImmediateVal info, 1 # (len, p)-pair
-        target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
-    # len:
-    target.addImmediateVal info, 1
-    buildTyped target, info, FieldAt, typeToIr(c.m, arrType):
-      copyTree target, tmp
-      target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0
-
-  of tyArray:
-    let t = typeToIr(c.m, arrType)
-    target.addImmediateVal info, 0
-    buildTyped target, info, AddrOf, elemType:
-      buildTyped target, info, ArrayAt, t:
-        copyTree target, tmp
-        target.addIntVal c.lit.numbers, info, c.m.nativeIntId, 0
-    target.addImmediateVal info, 1
-    target.addIntVal(c.lit.numbers, info, c.m.nativeIntId, toInt lengthOrd(c.config, arrType))
-  else:
-    raiseAssert "addAddrOfFirstElem: " & typeToString(typ)
-
-proc genToOpenArrayConv(c: var ProcCon; arg: PNode; d: var Value; flags: GenFlags; destType: PType) =
-  let info = toLineInfo(c, arg.info)
-  let tmp = c.genx(arg, flags)
-  let arrType = destType.skipTypes(abstractVar)
-  template body(target) =
-    buildTyped target, info, ObjConstr, typeToIr(c.m, arrType):
-      c.addAddrOfFirstElem target, info, tmp, arg.typ
-
-  valueIntoDest c, info, d, arrType, body
-  freeTemp c, tmp
-
-proc genConv(c: var ProcCon; n, arg: PNode; d: var Value; flags: GenFlags; opc: Opcode) =
-  let targetType = n.typ.skipTypes({tyDistinct})
-  let argType = arg.typ.skipTypes({tyDistinct})
-
-  if sameBackendType(targetType, argType) or (
-      argType.kind == tyProc and targetType.kind == argType.kind):
-    # don't do anything for lambda lifting conversions:
-    gen c, arg, d
-    return
-
-  if opc != Cast and targetType.skipTypes({tyVar, tyLent}).kind in {tyOpenArray, tyVarargs} and
-      argType.skipTypes({tyVar, tyLent}).kind notin {tyOpenArray, tyVarargs}:
-    genToOpenArrayConv c, arg, d, flags, n.typ
-    return
-
-  let info = toLineInfo(c, n.info)
-  let tmp = c.genx(arg, flags)
-  template body(target) =
-    buildTyped target, info, opc, typeToIr(c.m, n.typ):
-      if opc == CheckedObjConv:
-        target.addLabel info, CheckedGoto, c.exitLabel
-      copyTree target, tmp
-
-  valueIntoDest c, info, d, n.typ, body
-  freeTemp c, tmp
-
-proc genObjOrTupleConstr(c: var ProcCon; n: PNode; d: var Value; t: PType) =
-  # XXX x = (x.old, 22)  produces wrong code ... stupid self assignments
-  let info = toLineInfo(c, n.info)
-  template body(target) =
-    buildTyped target, info, ObjConstr, typeToIr(c.m, t):
-      for i in ord(n.kind == nkObjConstr)..<n.len:
-        let it = n[i]
-        if it.kind == nkExprColonExpr:
-          genField(c, it[0], Value target)
-          let tmp = c.genx(it[1])
-          copyTree target, tmp
-          c.freeTemp(tmp)
-        else:
-          let tmp = c.genx(it)
-          target.addImmediateVal info, i
-          copyTree target, tmp
-          c.freeTemp(tmp)
-
-      if isException(t):
-        target.addImmediateVal info, 1 # "name" field is at position after the "parent". See system.nim
-        target.addStrVal c.lit.strings, info, t.skipTypes(abstractInst).sym.name.s
-
-  constrIntoDest c, info, d, t, body
-
-proc genRefObjConstr(c: var ProcCon; n: PNode; d: var Value) =
-  if isEmpty(d): d = getTemp(c, n)
-  let info = toLineInfo(c, n.info)
-  let refType = n.typ.skipTypes(abstractInstOwned)
-  let objType = refType.elementType
-
-  rawGenNew(c, d, refType, n.info, needsInit = nfAllFieldsSet notin n.flags)
-  var deref = default(Value)
-  deref.buildTyped info, Load, typeToIr(c.m, objType):
-    deref.Tree.copyTree d
-  genObjOrTupleConstr c, n, deref, objType
-
-proc genSeqConstr(c: var ProcCon; n: PNode; d: var Value) =
-  if isEmpty(d): d = getTemp(c, n)
-
-  let info = toLineInfo(c, n.info)
-  let seqtype = skipTypes(n.typ, abstractVarRange)
-  let baseType = seqtype.elementType
-
-  var b = default(Value)
-  b.addIntVal c.lit.numbers, info, c.m.nativeIntId, n.len
-
-  genNewSeqPayload(c, info, d, b, seqtype)
-
-  for i in 0..<n.len:
-    var dd = default(Value)
-    buildTyped dd, info, DerefArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, seqtype)[1]:
-      buildTyped dd, info, FieldAt, typeToIr(c.m, seqtype):
-        copyTree Tree(dd), d
-        dd.addImmediateVal info, 1 # (len, p)-pair
-      dd.addIntVal c.lit.numbers, info, c.m.nativeIntId, i
-    gen(c, n[i], dd)
-
-  freeTemp c, d
-
-proc genArrayConstr(c: var ProcCon; n: PNode, d: var Value) =
-  let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc})
-  if seqType.kind == tySequence:
-    genSeqConstr(c, n, d)
-    return
-
-  let info = toLineInfo(c, n.info)
-  template body(target) =
-    buildTyped target, info, ArrayConstr, typeToIr(c.m, n.typ):
-      for i in 0..<n.len:
-        let tmp = c.genx(n[i])
-        copyTree target, tmp
-        c.freeTemp(tmp)
-
-  constrIntoDest c, info, d, n.typ, body
-
-proc genAsgn2(c: var ProcCon; a, b: PNode) =
-  assert a != nil
-  assert b != nil
-  var d = c.genx(a)
-  c.gen b, d
-
-proc irModule(c: var ProcCon; owner: PSym): string =
-  #if owner == c.m.module: "" else:
-  customPath(toFullPath(c.config, owner.info))
-
-proc fromForeignModule(c: ProcCon; s: PSym): bool {.inline.} =
-  result = ast.originatingModule(s) != c.m.module and not c.m.noModularity
-
-proc genForeignVar(c: var ProcCon; s: PSym) =
-  var opc: Opcode
-  if s.kind == skConst:
-    opc = SummonConst
-  elif sfThread in s.flags:
-    opc = SummonThreadLocal
-  else:
-    assert sfGlobal in s.flags
-    opc = SummonGlobal
-  let t = typeToIr(c.m, s.typ)
-  let info = toLineInfo(c, s.info)
-  build c.code, info, ForeignDecl:
-    buildTyped c.code, info, opc, t:
-      build c.code, info, ModuleSymUse:
-        c.code.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(s))
-        c.code.addImmediateVal info, s.itemId.item.int
-
-proc genVarSection(c: var ProcCon; n: PNode) =
-  for a in n:
-    if a.kind == nkCommentStmt: continue
-    #assert(a[0].kind == nkSym) can happen for transformed vars
-    if a.kind == nkVarTuple:
-      c.gen(lowerTupleUnpacking(c.m.graph, a, c.m.idgen, c.prc))
-    else:
-      var vn = a[0]
-      if vn.kind == nkPragmaExpr: vn = vn[0]
-      if vn.kind == nkSym:
-        let s = vn.sym
-        if s.kind == skConst:
-          if dontInlineConstant(n, s.astdef):
-            let symId = toSymId(c, s)
-            c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(s.name.s)
-            let val = c.genx(s.astdef)
-            let info = toLineInfo(c, a.info)
-            buildTyped c.code, info, SummonConst, typeToIr(c.m, s.typ):
-              c.code.addSymDef info, symId
-              c.code.copyTree val
-            freeTemp c, val
-        else:
-          var opc: Opcode
-          if sfThread in s.flags:
-            opc = SummonThreadLocal
-          elif sfGlobal in s.flags:
-            opc = SummonGlobal
-          else:
-            opc = Summon
-          #assert t.int >= 0, typeToString(s.typ) & (c.config $ n.info)
-          let symId = toSymId(c, s)
-          c.code.addSummon toLineInfo(c, a.info), symId, typeToIr(c.m, s.typ), opc
-          c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(s.name.s)
-          if a[2].kind != nkEmpty:
-            genAsgn2(c, vn, a[2])
-      else:
-        if a[2].kind == nkEmpty:
-          genAsgn2(c, vn, expandDefault(vn.typ, vn.info))
-        else:
-          genAsgn2(c, vn, a[2])
-
-proc genAsgn(c: var ProcCon; n: PNode) =
-  var d = c.genx(n[0])
-  c.gen n[1], d
-
-proc convStrToCStr(c: var ProcCon; n: PNode; d: var Value) =
-  genUnaryCp(c, n, d, "nimToCStringConv", argAt = 0)
-
-proc convCStrToStr(c: var ProcCon; n: PNode; d: var Value) =
-  genUnaryCp(c, n, d, "cstrToNimstr", argAt = 0)
-
-proc genRdVar(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
-  let info = toLineInfo(c, n.info)
-  let s = n.sym
-  if fromForeignModule(c, s):
-    if s.kind in {skVar, skConst, skLet} and not c.m.pendingVarsAsSet.containsOrIncl(s.itemId):
-      c.m.pendingVars.add s
-
-    template body(target) =
-      build target, info, ModuleSymUse:
-        target.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(s))
-        target.addImmediateVal info, s.itemId.item.int
-
-    valueIntoDest c, info, d, s.typ, body
-  else:
-    template body(target) =
-      target.addSymUse info, toSymId(c, s)
-    valueIntoDest c, info, d, s.typ, body
-
-proc genSym(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
-  let s = n.sym
-  case s.kind
-  of skConst:
-    if dontInlineConstant(n, s.astdef):
-      genRdVar(c, n, d, flags)
-    else:
-      gen(c, s.astdef, d, flags)
-  of skVar, skForVar, skTemp, skLet, skResult, skParam:
-    genRdVar(c, n, d, flags)
-  of skProc, skFunc, skConverter, skMethod, skIterator:
-    if not c.m.noModularity:
-      # anon and generic procs have no AST so we need to remember not to forget
-      # to emit these:
-      if not c.m.processedProcs.contains(s.itemId):
-        if not c.m.pendingProcsAsSet.containsOrIncl(s.itemId):
-          c.m.pendingProcs.add s
-    genRdVar(c, n, d, flags)
-  of skEnumField:
-    let info = toLineInfo(c, n.info)
-    template body(target) =
-      target.addIntVal c.lit.numbers, info, typeToIr(c.m, n.typ), s.position
-    valueIntoDest c, info, d, n.typ, body
-  else:
-    localError(c.config, n.info, "cannot generate code for: " & s.name.s)
-
-proc genNumericLit(c: var ProcCon; n: PNode; d: var Value; bits: int64) =
-  let info = toLineInfo(c, n.info)
-  template body(target) =
-    target.addIntVal c.lit.numbers, info, typeToIr(c.m, n.typ), bits
-  valueIntoDest c, info, d, n.typ, body
-
-proc genStringLit(c: var ProcCon; n: PNode; d: var Value) =
-  let info = toLineInfo(c, n.info)
-  template body(target) =
-    target.addStrVal c.lit.strings, info, n.strVal
-  valueIntoDest c, info, d, n.typ, body
-
-proc genNilLit(c: var ProcCon; n: PNode; d: var Value) =
-  let info = toLineInfo(c, n.info)
-  template body(target) =
-    target.addNilVal info, typeToIr(c.m, n.typ)
-  valueIntoDest c, info, d, n.typ, body
-
-proc genRangeCheck(c: var ProcCon; n: PNode; d: var Value) =
-  if optRangeCheck in c.options:
-    let info = toLineInfo(c, n.info)
-    let tmp = c.genx n[0]
-    let a = c.genx n[1]
-    let b = c.genx n[2]
-    template body(target) =
-      buildTyped target, info, CheckedRange, typeToIr(c.m, n.typ):
-        target.addLabel info, CheckedGoto, c.exitLabel
-        copyTree target, tmp
-        copyTree target, a
-        copyTree target, b
-    valueIntoDest c, info, d, n.typ, body
-    freeTemp c, tmp
-    freeTemp c, a
-    freeTemp c, b
-  else:
-    gen c, n[0], d
-
-proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
-  let arrayType = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc})
-  let arrayKind = arrayType.kind
-  let info = toLineInfo(c, n.info)
-  case arrayKind
-  of tyString:
-    let a = genx(c, n[0], flags)
-    let b = genIndexCheck(c, n[1], a, ForStr, arrayType)
-    let t = typeToIr(c.m, n.typ)
-    template body(target) =
-      buildTyped target, info, DerefArrayAt, c.m.strPayloadId[1]:
-        buildTyped target, info, FieldAt, typeToIr(c.m, arrayType):
-          copyTree target, a
-          target.addImmediateVal info, 1 # (len, p)-pair
-        copyTree target, b
-    intoDest d, info, t, body
-    freeTemp c, b
-    freeTemp c, a
-
-  of tyCstring, tyPtr, tyUncheckedArray:
-    let a = genx(c, n[0], flags)
-    let b = genx(c, n[1])
-    template body(target) =
-      buildTyped target, info, DerefArrayAt, typeToIr(c.m, arrayType):
-        copyTree target, a
-        copyTree target, b
-    valueIntoDest c, info, d, n.typ, body
-
-    freeTemp c, b
-    freeTemp c, a
-  of tyTuple:
-    let a = genx(c, n[0], flags)
-    let b = int n[1].intVal
-    template body(target) =
-      buildTyped target, info, FieldAt, typeToIr(c.m, arrayType):
-        copyTree target, a
-        target.addImmediateVal info, b
-    valueIntoDest c, info, d, n.typ, body
-
-    freeTemp c, a
-  of tyOpenArray, tyVarargs:
-    let a = genx(c, n[0], flags)
-    let b = genIndexCheck(c, n[1], a, ForOpenArray, arrayType)
-    let t = typeToIr(c.m, n.typ)
-    template body(target) =
-      buildTyped target, info, DerefArrayAt, openArrayPayloadType(c.m.types, c.m.nirm.types, n[0].typ):
-        buildTyped target, info, FieldAt, typeToIr(c.m, arrayType):
-          copyTree target, a
-          target.addImmediateVal info, 0 # (p, len)-pair
-        copyTree target, b
-    intoDest d, info, t, body
-
-    freeTemp c, b
-    freeTemp c, a
-  of tyArray:
-    let a = genx(c, n[0], flags)
-    var b = default(Value)
-    genIndex(c, n[1], n[0].typ, b)
-
-    template body(target) =
-      buildTyped target, info, ArrayAt, typeToIr(c.m, arrayType):
-        copyTree target, a
-        copyTree target, b
-    valueIntoDest c, info, d, n.typ, body
-    freeTemp c, b
-    freeTemp c, a
-  of tySequence:
-    let a = genx(c, n[0], flags)
-    let b = genIndexCheck(c, n[1], a, ForSeq, arrayType)
-    let t = typeToIr(c.m, n.typ)
-    template body(target) =
-      buildTyped target, info, DerefArrayAt, seqPayloadPtrType(c.m.types, c.m.nirm.types, n[0].typ)[1]:
-        buildTyped target, info, FieldAt, t:
-          copyTree target, a
-          target.addImmediateVal info, 1 # (len, p)-pair
-        copyTree target, b
-    intoDest d, info, t, body
-    freeTemp c, b
-    freeTemp c, a
-  else:
-    localError c.config, n.info, "invalid type for nkBracketExpr: " & $arrayKind
-
-proc genObjAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) =
-  let info = toLineInfo(c, n.info)
-
-  var n0 = n[0]
-  var opc = FieldAt
-  if n0.kind == nkDotExpr:
-    # obj[].a --> DerefFieldAt instead of FieldAt:
-    n0 = n[0]
-    opc = DerefFieldAt
-
-  let a = genx(c, n0, flags)
-
-  template body(target) =
-    buildTyped target, info, opc, typeToIr(c.m, n0.typ):
-      copyTree target, a
-      genField c, n[1], Value(target)
-
-  valueIntoDest c, info, d, n.typ, body
-  freeTemp c, a
-
-proc genParams(c: var ProcCon; params: PNode; prc: PSym): PSym =
-  result = nil
-  if params.len > 0 and resultPos < prc.ast.len:
-    let resNode = prc.ast[resultPos]
-    result = resNode.sym # get result symbol
-    c.code.addSummon toLineInfo(c, result.info), toSymId(c, result),
-      typeToIr(c.m, result.typ), SummonResult
-  elif prc.typ.len > 0 and not isEmptyType(prc.typ.returnType) and not isCompileTimeOnly(prc.typ.returnType):
-    # happens for procs without bodies:
-    let t = typeToIr(c.m, prc.typ.returnType)
-    let tmp = allocTemp(c, t)
-    c.code.addSummon toLineInfo(c, params.info), tmp, t, SummonResult
-
-  for i in 1..<params.len:
-    let s = params[i].sym
-    if not isCompileTimeOnly(s.typ):
-      let t = typeToIr(c.m, s.typ)
-      assert t.int != -1, typeToString(s.typ)
-      let symId = toSymId(c, s)
-      c.code.addSummon toLineInfo(c, params[i].info), symId, t, SummonParam
-      c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(s.name.s)
-
-proc addCallConv(c: var ProcCon; info: PackedLineInfo; callConv: TCallingConvention) =
-  template ann(s: untyped) = c.code.addPragmaId info, s
-  case callConv
-  of ccNimCall, ccFastCall, ccClosure: ann FastCall
-  of ccStdCall: ann StdCall
-  of ccCDecl: ann CDeclCall
-  of ccSafeCall: ann SafeCall
-  of ccSysCall: ann SysCall
-  of ccInline: ann InlineCall
-  of ccNoInline: ann NoinlineCall
-  of ccThisCall: ann ThisCall
-  of ccNoConvention, ccMember: ann NoCall
-
-proc genProc(cOuter: var ProcCon; prc: PSym) =
-  if prc.magic notin generatedMagics: return
-  if cOuter.m.processedProcs.containsOrIncl(prc.itemId):
-    return
-  #assert cOuter.m.inProc == 0, " in nested proc! " & prc.name.s
-  if cOuter.m.inProc > 0:
-    if not cOuter.m.pendingProcsAsSet.containsOrIncl(prc.itemId):
-      cOuter.m.pendingProcs.add prc
-    return
-  inc cOuter.m.inProc
-
-  var c = initProcCon(cOuter.m, prc, cOuter.m.graph.config)
-  let body =
-    if not fromForeignModule(c, prc):
-      transformBody(c.m.graph, c.m.idgen, prc, {useCache, keepOpenArrayConversions})
-    else:
-      nil
-
-  let info = toLineInfo(c, prc.info)
-  build c.code, info, (if body != nil: ProcDecl else: ForeignProcDecl):
-    if body != nil:
-      let symId = toSymId(c, prc)
-      addSymDef c.code, info, symId
-      c.m.nirm.symnames[symId] = c.lit.strings.getOrIncl(prc.name.s)
-    else:
-      build c.code, info, ModuleSymUse:
-        c.code.addStrVal c.lit.strings, info, irModule(c, ast.originatingModule(prc))
-        c.code.addImmediateVal info, prc.itemId.item.int
-    addCallConv c, info, prc.typ.callConv
-    if sfCompilerProc in prc.flags:
-      build c.code, info, PragmaPair:
-        c.code.addPragmaId info, CoreName
-        c.code.addStrVal c.lit.strings, info, prc.name.s
-    if {sfImportc, sfExportc} * prc.flags != {}:
-      build c.code, info, PragmaPair:
-        c.code.addPragmaId info, ExternName
-        c.code.addStrVal c.lit.strings, info, prc.loc.r
-      if sfImportc in prc.flags:
-        if lfHeader in prc. loc.flags:
-          assert(prc. annex != nil)
-          let str = getStr(prc. annex.path)
-          build c.code, info, PragmaPair:
-            c.code.addPragmaId info, HeaderImport
-            c.code.addStrVal c.lit.strings, info, str
-        elif lfDynamicLib in prc. loc.flags:
-          assert(prc. annex != nil)
-          let str = getStr(prc. annex.path)
-          build c.code, info, PragmaPair:
-            c.code.addPragmaId info, DllImport
-            c.code.addStrVal c.lit.strings, info, str
-      elif sfExportc in prc.flags:
-        if lfDynamicLib in prc. loc.flags:
-          c.code.addPragmaId info, DllExport
-        else:
-          c.code.addPragmaId info, ObjExport
-
-    let resultSym = genParams(c, prc.typ.n, prc)
-    if body != nil:
-      gen(c, body)
-      patch c, body, c.exitLabel
-      if resultSym != nil:
-        build c.code, info, Ret:
-          c.code.addSymUse info, toSymId(c, resultSym)
-      else:
-        build c.code, info, Ret:
-          c.code.addNop info
-
-  #copyTree cOuter.code, c.code
-  dec cOuter.m.inProc
-
-proc genProc(cOuter: var ProcCon; n: PNode) =
-  if n.len == 0 or n[namePos].kind != nkSym: return
-  let prc = n[namePos].sym
-  if isGenericRoutineStrict(prc) or isCompileTimeProc(prc) or sfForward in prc.flags: return
-  genProc cOuter, prc
-
-proc genClosureCall(c: var ProcCon; n: PNode; d: var Value) =
-  let typ = skipTypes(n[0].typ, abstractInstOwned)
-  if tfIterator in typ.flags:
-    const PatIter = "$1.ClP_0($3, $1.ClE_0)" # we know the env exists
-
-  else:
-    const PatProc = "$1.ClE_0? $1.ClP_0($3, $1.ClE_0):(($4)($1.ClP_0))($2)"
-
-
-proc genComplexCall(c: var ProcCon; n: PNode; d: var Value) =
-  if n[0].typ != nil and n[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure:
-    # XXX genClosureCall p, n, d
-    genCall c, n, d
-  else:
-    genCall c, n, d
-
-proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) =
-  when defined(nimCompilerStacktraceHints):
-    setFrameMsg c.config$n.info & " " & $n.kind & " " & $flags
-  case n.kind
-  of nkSym: genSym(c, n, d, flags)
-  of nkCallKinds:
-    if n[0].kind == nkSym:
-      let s = n[0].sym
-      if s.magic != mNone:
-        genMagic(c, n, d, s.magic)
-      elif s.kind == skMethod:
-        localError(c.config, n.info, "cannot call method " & s.name.s &
-          " at compile time")
-      else:
-        genComplexCall(c, n, d)
-    else:
-      genComplexCall(c, n, d)
-  of nkCharLit..nkInt64Lit, nkUIntLit..nkUInt64Lit:
-    genNumericLit(c, n, d, n.intVal)
-  of nkFloatLit..nkFloat128Lit:
-    genNumericLit(c, n, d, cast[int64](n.floatVal))
-  of nkStrLit..nkTripleStrLit:
-    genStringLit(c, n, d)
-  of nkNilLit:
-    if not n.typ.isEmptyType: genNilLit(c, n, d)
-    else: unused(c, n, d)
-  of nkAsgn, nkFastAsgn, nkSinkAsgn:
-    unused(c, n, d)
-    genAsgn(c, n)
-  of nkDotExpr: genObjAccess(c, n, d, flags)
-  of nkCheckedFieldExpr: genObjAccess(c, n[0], d, flags)
-  of nkBracketExpr: genArrAccess(c, n, d, flags)
-  of nkDerefExpr, nkHiddenDeref: genDeref(c, n, d, flags)
-  of nkAddr, nkHiddenAddr: genAddr(c, n, d, flags)
-  of nkIfStmt, nkIfExpr: genIf(c, n, d)
-  of nkWhenStmt:
-    # This is "when nimvm" node. Chose the first branch.
-    gen(c, n[0][1], d)
-  of nkCaseStmt: genCase(c, n, d)
-  of nkWhileStmt:
-    unused(c, n, d)
-    genWhile(c, n)
-  of nkBlockExpr, nkBlockStmt: genBlock(c, n, d)
-  of nkReturnStmt: genReturn(c, n)
-  of nkRaiseStmt: genRaise(c, n)
-  of nkBreakStmt: genBreak(c, n)
-  of nkTryStmt, nkHiddenTryStmt: genTry(c, n, d)
-  of nkStmtList:
-    #unused(c, n, d)
-    # XXX Fix this bug properly, lexim triggers it
-    for x in n: gen(c, x)
-  of nkStmtListExpr:
-    for i in 0..<n.len-1: gen(c, n[i])
-    gen(c, n[^1], d, flags)
-  of nkPragmaBlock:
-    gen(c, n.lastSon, d, flags)
-  of nkDiscardStmt:
-    unused(c, n, d)
-    gen(c, n[0], d)
-  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
-    genConv(c, n, n[1], d, flags, NumberConv) # misnomer?
-  of nkObjDownConv:
-    genConv(c, n, n[0], d, flags, ObjConv)
-  of nkObjUpConv:
-    genConv(c, n, n[0], d, flags, CheckedObjConv)
-  of nkVarSection, nkLetSection, nkConstSection:
-    unused(c, n, d)
-    genVarSection(c, n)
-  of nkLambdaKinds:
-    #let s = n[namePos].sym
-    #discard genProc(c, s)
-    gen(c, newSymNode(n[namePos].sym), d)
-  of nkChckRangeF, nkChckRange64, nkChckRange:
-    genRangeCheck(c, n, d)
-  of declarativeDefs - {nkIteratorDef}:
-    unused(c, n, d)
-    genProc(c, n)
-  of nkEmpty, nkCommentStmt, nkTypeSection, nkPragma,
-     nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt,
-     nkMixinStmt, nkBindStmt, nkMacroDef, nkIteratorDef:
-    unused(c, n, d)
-  of nkStringToCString: convStrToCStr(c, n, d)
-  of nkCStringToString: convCStrToStr(c, n, d)
-  of nkBracket: genArrayConstr(c, n, d)
-  of nkCurly: genSetConstr(c, n, d)
-  of nkObjConstr:
-    if n.typ.skipTypes(abstractInstOwned).kind == tyRef:
-      genRefObjConstr(c, n, d)
-    else:
-      genObjOrTupleConstr(c, n, d, n.typ)
-  of nkPar, nkClosure, nkTupleConstr:
-    genObjOrTupleConstr(c, n, d, n.typ)
-  of nkCast:
-    genConv(c, n, n[1], d, flags, Cast)
-  of nkComesFrom:
-    discard "XXX to implement for better stack traces"
-  #of nkState: genState(c, n)
-  #of nkGotoState: genGotoState(c, n)
-  #of nkBreakState: genBreakState(c, n, d)
-  else:
-    localError(c.config, n.info, "cannot generate IR code for " & $n)
-
-proc genPendingProcs(c: var ProcCon) =
-  while c.m.pendingProcs.len > 0 or c.m.pendingVars.len > 0:
-    let procs = move(c.m.pendingProcs)
-    for v in procs:
-      genProc(c, v)
-    let vars = move(c.m.pendingVars)
-    for v in vars:
-      genForeignVar(c, v)
-
-proc genStmt*(c: var ProcCon; n: PNode): NodePos =
-  result = NodePos c.code.len
-  var d = default(Value)
-  c.gen(n, d)
-  unused c, n, d
-  genPendingProcs c
-
-proc genExpr*(c: var ProcCon; n: PNode, requiresValue = true): int =
-  result = c.code.len
-  var d = default(Value)
-  c.gen(n, d)
-  genPendingProcs c
-  if isEmpty d:
-    if requiresValue:
-      globalError(c.config, n.info, "VM problem: d register is not set")
diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim
deleted file mode 100644
index 01f9c4c9a..000000000
--- a/compiler/nir/cir.nim
+++ /dev/null
@@ -1,983 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2023 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-# We produce C code as a list of tokens.
-
-import std / [assertions, syncio, tables, intsets, formatfloat]
-from std / strutils import toOctal
-import .. / ic / [bitabs, rodfiles]
-import nirtypes, nirinsts, nirfiles
-import ../../dist/checksums/src/checksums/md5
-
-type
-  Token = LitId # indexing into the tokens BiTable[string]
-
-  PredefinedToken = enum
-    IgnoreMe = "<unused>"
-    EmptyToken = ""
-    CurlyLe = "{"
-    CurlyRi = "}"
-    ParLe = "("
-    ParRi = ")"
-    BracketLe = "["
-    BracketRi = "]"
-    NewLine = "\n"
-    Semicolon = ";"
-    Comma = ", "
-    Space = " "
-    Colon = ": "
-    Dot = "."
-    Arrow = "->"
-    Star = "*"
-    Amp = "&"
-    AsgnOpr = " = "
-    ScopeOpr = "::"
-    ConstKeyword = "const "
-    StaticKeyword = "static "
-    ExternKeyword = "extern "
-    WhileKeyword = "while "
-    IfKeyword = "if ("
-    ElseKeyword = "else "
-    SwitchKeyword = "switch "
-    CaseKeyword = "case "
-    DefaultKeyword = "default:"
-    BreakKeyword = "break"
-    NullPtr = "nullptr"
-    IfNot = "if (!("
-    ReturnKeyword = "return "
-    TypedefStruct = "typedef struct "
-    TypedefUnion = "typedef union "
-    IncludeKeyword = "#include "
-
-proc fillTokenTable(tab: var BiTable[string]) =
-  for e in EmptyToken..high(PredefinedToken):
-    let id = tab.getOrIncl $e
-    assert id == LitId(e), $(id, " ", ord(e))
-
-type
-  GeneratedCode* = object
-    m: NirModule
-    includes: seq[LitId]
-    includedHeaders: IntSet
-    data: seq[LitId]
-    protos: seq[LitId]
-    code: seq[LitId]
-    init: seq[LitId]
-    tokens: BiTable[string]
-    emittedStrings: IntSet
-    needsPrefix: IntSet
-    generatedTypes: IntSet
-    mangledModules: Table[LitId, LitId]
-
-proc initGeneratedCode*(m: sink NirModule): GeneratedCode =
-  result = GeneratedCode(m: m, code: @[], tokens: initBiTable[string]())
-  fillTokenTable(result.tokens)
-
-proc add*(g: var GeneratedCode; t: PredefinedToken) {.inline.} =
-  g.code.add Token(t)
-
-proc add*(g: var GeneratedCode; s: string) {.inline.} =
-  g.code.add g.tokens.getOrIncl(s)
-
-proc mangleModuleName(c: var GeneratedCode; key: LitId): LitId =
-  result = c.mangledModules.getOrDefault(key, LitId(0))
-  if result == LitId(0):
-    let u {.cursor.} = c.m.lit.strings[key]
-    var last = u.len - len(".nim") - 1
-    var start = last
-    while start >= 0 and u[start] != '/': dec start
-    var sum = getMD5(u)
-    sum.setLen(8)
-    let dest = u.substr(start+1, last) & sum
-    result = c.tokens.getOrIncl(dest)
-    c.mangledModules[key] = result
-
-type
-  CppFile = object
-    f: File
-
-proc write(f: var CppFile; s: string) = write(f.f, s)
-proc write(f: var CppFile; c: char) = write(f.f, c)
-
-proc writeTokenSeq(f: var CppFile; s: seq[Token]; c: GeneratedCode) =
-  var indent = 0
-  for i in 0..<s.len:
-    let x = s[i]
-    case x
-    of Token(CurlyLe):
-      inc indent
-      write f, c.tokens[x]
-      write f, "\n"
-      for i in 1..indent*2: write f, ' '
-    of Token(CurlyRi):
-      dec indent
-      write f, c.tokens[x]
-      if i+1 < s.len and s[i+1] == Token(CurlyRi):
-        discard
-      else:
-        write f, "\n"
-        for i in 1..indent*2: write f, ' '
-    of Token(Semicolon):
-      write f, c.tokens[x]
-      if i+1 < s.len and s[i+1] == Token(CurlyRi):
-        discard "no newline before }"
-      else:
-        write f, "\n"
-        for i in 1..indent*2: write f, ' '
-    of Token(NewLine):
-      write f, c.tokens[x]
-      for i in 1..indent*2: write f, ' '
-    else:
-      write f, c.tokens[x]
-
-
-# Type graph
-
-type
-  TypeList = object
-    processed: IntSet
-    s: seq[(TypeId, PredefinedToken)]
-
-proc add(dest: var TypeList; elem: TypeId; decl: PredefinedToken) =
-  if not containsOrIncl(dest.processed, int(elem)):
-    dest.s.add (elem, decl)
-
-type
-  TypeOrder = object
-    forwardedDecls, ordered: TypeList
-    typeImpls: Table[string, TypeId]
-    lookedAt: IntSet
-
-proc traverseObject(types: TypeGraph; lit: Literals; c: var TypeOrder; t: TypeId)
-
-proc recordDependency(types: TypeGraph; lit: Literals; c: var TypeOrder; parent, child: TypeId) =
-  var ch = child
-  var viaPointer = false
-  while true:
-    case types[ch].kind
-    of APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy:
-      viaPointer = true
-      ch = elementType(types, ch)
-    of LastArrayTy:
-      ch = elementType(types, ch)
-    else:
-      break
-
-  case types[ch].kind
-  of ObjectTy, UnionTy:
-    let decl = if types[ch].kind == ObjectTy: TypedefStruct else: TypedefUnion
-    let obj = c.typeImpls.getOrDefault(lit.strings[types[ch].litId])
-    if viaPointer:
-      c.forwardedDecls.add obj, decl
-    else:
-      if not containsOrIncl(c.lookedAt, obj.int):
-        traverseObject(types, lit, c, obj)
-      c.ordered.add obj, decl
-  of ArrayTy:
-    if viaPointer:
-      c.forwardedDecls.add ch, TypedefStruct
-    else:
-      if not containsOrIncl(c.lookedAt, ch.int):
-        traverseObject(types, lit, c, ch)
-      c.ordered.add ch, TypedefStruct
-  else:
-    discard "uninteresting type as we only focus on the required struct declarations"
-
-proc traverseObject(types: TypeGraph; lit: Literals; c: var TypeOrder; t: TypeId) =
-  for x in sons(types, t):
-    case types[x].kind
-    of FieldDecl:
-      recordDependency types, lit, c, t, x.firstSon
-    of ObjectTy:
-      # inheritance
-      recordDependency types, lit, c, t, x
-    else: discard
-
-proc traverseTypes(types: TypeGraph; lit: Literals; c: var TypeOrder) =
-  for t in allTypes(types):
-    if types[t].kind in {ObjectDecl, UnionDecl}:
-      assert types[t.firstSon].kind == NameVal
-      c.typeImpls[lit.strings[types[t.firstSon].litId]] = t
-
-  for t in allTypesIncludingInner(types):
-    case types[t].kind
-    of ObjectDecl, UnionDecl:
-      traverseObject types, lit, c, t
-      let decl = if types[t].kind == ObjectDecl: TypedefStruct else: TypedefUnion
-      c.ordered.add t, decl
-    of ArrayTy:
-      traverseObject types, lit, c, t
-      c.ordered.add t, TypedefStruct
-    else: discard
-
-when false:
-  template emitType(s: string) = c.types.add c.tokens.getOrIncl(s)
-  template emitType(t: Token) = c.types.add t
-  template emitType(t: PredefinedToken) = c.types.add Token(t)
-
-proc genType(g: var GeneratedCode; types: TypeGraph; lit: Literals; t: TypeId; name = "") =
-  template maybeAddName =
-    if name != "":
-      g.add Space
-      g.add name
-
-  template atom(s: string) =
-    g.add s
-    maybeAddName()
-  case types[t].kind
-  of VoidTy: atom "void"
-  of IntTy: atom "NI" & $types[t].integralBits
-  of UIntTy: atom "NU" & $types[t].integralBits
-  of FloatTy: atom "NF" & $types[t].integralBits
-  of BoolTy: atom "NB" & $types[t].integralBits
-  of CharTy: atom "NC" & $types[t].integralBits
-  of ObjectTy, UnionTy, NameVal, AnnotationVal:
-    atom lit.strings[types[t].litId]
-  of VarargsTy:
-    g.add "..."
-  of APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy:
-    genType g, types, lit, elementType(types, t)
-    g.add Star
-    maybeAddName()
-  of ArrayTy:
-    genType g, types, lit, arrayName(types, t)
-    maybeAddName()
-  of LastArrayTy:
-    genType g, types, lit, elementType(types, t)
-    maybeAddName()
-    g.add BracketLe
-    g.add BracketRi
-  of ProcTy:
-    let (retType, callConv) = returnType(types, t)
-    genType g, types, lit, retType
-    g.add Space
-    g.add ParLe
-    genType g, types, lit, callConv
-    g.add Star # "(*fn)"
-    maybeAddName()
-    g.add ParRi
-    g.add ParLe
-    var i = 0
-    for ch in params(types, t):
-      if i > 0: g.add Comma
-      genType g, types, lit, ch
-      inc i
-    g.add ParRi
-  of ObjectDecl, UnionDecl:
-    atom lit.strings[types[t.firstSon].litId]
-  of IntVal, SizeVal, AlignVal, OffsetVal, FieldDecl:
-    #raiseAssert "did not expect: " & $types[t].kind
-    g.add "BUG "
-    atom $types[t].kind
-
-proc generateTypes(g: var GeneratedCode; types: TypeGraph; lit: Literals; c: TypeOrder) =
-  for (t, declKeyword) in c.forwardedDecls.s:
-    let name = if types[t].kind == ArrayTy: arrayName(types, t) else: t.firstSon
-    let s {.cursor.} = lit.strings[types[name].litId]
-    g.add declKeyword
-    g.add s
-    g.add Space
-    g.add s
-    g.add Semicolon
-
-  for (t, declKeyword) in c.ordered.s:
-    let name = if types[t].kind == ArrayTy: arrayName(types, t) else: t.firstSon
-    let litId = types[name].litId
-    if not g.generatedTypes.containsOrIncl(litId.int):
-      let s {.cursor.} = lit.strings[litId]
-      g.add declKeyword
-      g.add CurlyLe
-      if types[t].kind == ArrayTy:
-        genType g, types, lit, elementType(types, t), "a"
-        g.add BracketLe
-        g.add $arrayLen(types, t)
-        g.add BracketRi
-        g.add Semicolon
-      else:
-        var i = 0
-        for x in sons(types, t):
-          case types[x].kind
-          of FieldDecl:
-            genType g, types, lit, x.firstSon, "F" & $i
-            g.add Semicolon
-            inc i
-          of ObjectTy:
-            genType g, types, lit, x, "P"
-            g.add Semicolon
-          else: discard
-      g.add CurlyRi
-      g.add s
-      g.add Semicolon
-
-# Procs
-
-proc toCChar*(c: char; result: var string) {.inline.} =
-  case c
-  of '\0'..'\x1F', '\x7F'..'\xFF':
-    result.add '\\'
-    result.add toOctal(c)
-  of '\'', '\"', '\\', '?':
-    result.add '\\'
-    result.add c
-  else:
-    result.add c
-
-proc makeCString(s: string): string =
-  result = newStringOfCap(s.len + 10)
-  result.add('"')
-  for c in s: toCChar(c, result)
-  result.add('"')
-
-template emitData(s: string) = c.data.add c.tokens.getOrIncl(s)
-template emitData(t: Token) = c.data.add t
-template emitData(t: PredefinedToken) = c.data.add Token(t)
-
-proc genStrLit(c: var GeneratedCode; lit: Literals; litId: LitId): Token =
-  result = Token(c.tokens.getOrIncl "QStr" & $litId)
-  if not containsOrIncl(c.emittedStrings, int(litId)):
-    let s {.cursor.} = lit.strings[litId]
-    emitData "static const struct "
-    emitData CurlyLe
-    emitData "NI cap"
-    emitData Semicolon
-    emitData "NC8 data"
-    emitData BracketLe
-    emitData $s.len
-    emitData "+1"
-    emitData BracketRi
-    emitData Semicolon
-    emitData CurlyRi
-    emitData result
-    emitData AsgnOpr
-    emitData CurlyLe
-    emitData $s.len
-    emitData " | NIM_STRLIT_FLAG"
-    emitData Comma
-    emitData makeCString(s)
-    emitData CurlyRi
-    emitData Semicolon
-
-proc genIntLit(c: var GeneratedCode; lit: Literals; litId: LitId) =
-  let i = lit.numbers[litId]
-  if i > low(int32) and i <= high(int32):
-    c.add $i
-  elif i == low(int32):
-    # Nim has the same bug for the same reasons :-)
-    c.add "(-2147483647 -1)"
-  elif i > low(int64):
-    c.add "IL64("
-    c.add $i
-    c.add ")"
-  else:
-    c.add "(IL64(-9223372036854775807) - IL64(1))"
-
-proc gen(c: var GeneratedCode; t: Tree; n: NodePos)
-
-proc genDisplayName(c: var GeneratedCode; symId: SymId) =
-  let displayName = c.m.symnames[symId]
-  if displayName != LitId(0):
-    c.add "/*"
-    c.add c.m.lit.strings[displayName]
-    c.add "*/"
-
-proc genSymDef(c: var GeneratedCode; t: Tree; n: NodePos) =
-  if t[n].kind == SymDef:
-    let symId = t[n].symId
-    c.needsPrefix.incl symId.int
-    genDisplayName c, symId
-  gen c, t, n
-
-proc genGlobal(c: var GeneratedCode; t: Tree; name, typ: NodePos; annotation: string) =
-  c.add annotation
-  let m: string
-  if t[name].kind == SymDef:
-    let symId = t[name].symId
-    m = c.tokens[mangleModuleName(c, c.m.namespace)] & "__" & $symId
-    genDisplayName c, symId
-  else:
-    assert t[name].kind == ModuleSymUse
-    let (x, y) = sons2(t, name)
-    m = c.tokens[mangleModuleName(c, t[x].litId)] & "__" & $t[y].immediateVal
-  genType c, c.m.types, c.m.lit, t[typ].typeId, m
-
-proc genLocal(c: var GeneratedCode; t: Tree; name, typ: NodePos; annotation: string) =
-  assert t[name].kind == SymDef
-  c.add annotation
-  let symId = t[name].symId
-  genType c, c.m.types, c.m.lit, t[typ].typeId, "q" & $symId
-  genDisplayName c, symId
-
-proc genProcDecl(c: var GeneratedCode; t: Tree; n: NodePos; isExtern: bool) =
-  let signatureBegin = c.code.len
-  let name = n.firstSon
-
-  var prc = n.firstSon
-  next t, prc
-
-  while true:
-    case t[prc].kind
-    of PragmaPair:
-      let (x, y) = sons2(t, prc)
-      let key = cast[PragmaKey](t[x].rawOperand)
-      case key
-      of HeaderImport:
-        let lit = t[y].litId
-        let headerAsStr {.cursor.} = c.m.lit.strings[lit]
-        let header = c.tokens.getOrIncl(headerAsStr)
-        # headerAsStr can be empty, this has the semantics of the `nodecl` pragma:
-        if headerAsStr.len > 0 and not c.includedHeaders.containsOrIncl(int header):
-          if headerAsStr[0] == '#':
-            discard "skip the #include"
-          else:
-            c.includes.add Token(IncludeKeyword)
-          c.includes.add header
-          c.includes.add Token NewLine
-        # do not generate code for importc'ed procs:
-        return
-      of DllImport:
-        let lit = t[y].litId
-        raiseAssert "cannot eval: " & c.m.lit.strings[lit]
-      else: discard
-    of PragmaId: discard
-    else: break
-    next t, prc
-
-  var resultDeclPos = NodePos(-1)
-  if t[prc].kind == SummonResult:
-    resultDeclPos = prc
-    gen c, t, prc.firstSon
-    next t, prc
-  else:
-    c.add "void"
-  c.add Space
-  genSymDef c, t, name
-  c.add ParLe
-  var params = 0
-  while t[prc].kind == SummonParam:
-    if params > 0: c.add Comma
-    let (typ, sym) = sons2(t, prc)
-    genLocal c, t, sym, typ, ""
-    next t, prc
-    inc params
-  if params == 0:
-    c.add "void"
-  c.add ParRi
-
-  for i in signatureBegin ..< c.code.len:
-    c.protos.add c.code[i]
-  c.protos.add Token Semicolon
-
-  if isExtern:
-    c.code.setLen signatureBegin
-  else:
-    c.add CurlyLe
-    if resultDeclPos.int >= 0:
-      gen c, t, resultDeclPos
-    for ch in sonsRest(t, n, prc):
-      gen c, t, ch
-    c.add CurlyRi
-
-template triop(opr) =
-  let (typ, a, b) = sons3(t, n)
-  c.add ParLe
-  c.add ParLe
-  gen c, t, typ
-  c.add ParRi
-  gen c, t, a
-  c.add opr
-  gen c, t, b
-  c.add ParRi
-
-template cmpop(opr) =
-  let (_, a, b) = sons3(t, n)
-  c.add ParLe
-  gen c, t, a
-  c.add opr
-  gen c, t, b
-  c.add ParRi
-
-template binaryop(opr) =
-  let (typ, a) = sons2(t, n)
-  c.add ParLe
-  c.add ParLe
-  gen c, t, typ
-  c.add ParRi
-  c.add opr
-  gen c, t, a
-  c.add ParRi
-
-template checkedBinaryop(opr) =
-  let (typ, labIdx, a, b) = sons4(t, n)
-  let bits = integralBits(c.m.types[t[typ].typeId])
-  let lab = t[labIdx].label
-
-  c.add (opr & $bits)
-  c.add ParLe
-  c.gen t, a
-  c.add Comma
-  c.gen t, b
-  c.add Comma
-  c.add "L" & $lab.int
-  c.add ParRi
-
-proc genNumberConv(c: var GeneratedCode; t: Tree; n: NodePos) =
-  let (typ, arg) = sons2(t, n)
-  if t[arg].kind == IntVal:
-    let litId = t[arg].litId
-    c.add ParLe
-    c.add ParLe
-    gen c, t, typ
-    c.add ParRi
-    case c.m.types[t[typ].typeId].kind
-    of UIntTy:
-      let x = cast[uint64](c.m.lit.numbers[litId])
-      c.add $x
-    of FloatTy:
-      let x = cast[float64](c.m.lit.numbers[litId])
-      c.add $x
-    else:
-      gen c, t, arg
-    c.add ParRi
-  else:
-    binaryop ""
-
-template moveToDataSection(body: untyped) =
-  let oldLen = c.code.len
-  body
-  for i in oldLen ..< c.code.len:
-    c.data.add c.code[i]
-  setLen c.code, oldLen
-
-proc gen(c: var GeneratedCode; t: Tree; n: NodePos) =
-  case t[n].kind
-  of Nop:
-    discard "nothing to emit"
-  of ImmediateVal:
-    c.add $t[n].immediateVal
-  of IntVal:
-    genIntLit c, c.m.lit, t[n].litId
-  of StrVal:
-    c.code.add genStrLit(c, c.m.lit, t[n].litId)
-  of Typed:
-    genType c, c.m.types, c.m.lit, t[n].typeId
-  of SymDef, SymUse:
-    let s = t[n].symId
-    if c.needsPrefix.contains(s.int):
-      c.code.add mangleModuleName(c, c.m.namespace)
-      c.add "__"
-      c.add $s
-    else:
-      # XXX Use proper names here
-      c.add "q"
-      c.add $s
-
-  of ModuleSymUse:
-    let (x, y) = sons2(t, n)
-    let u = mangleModuleName(c, t[x].litId)
-    let s = t[y].immediateVal
-    c.code.add u
-    c.add "__"
-    c.add $s
-
-  of NilVal:
-    c.add NullPtr
-  of LoopLabel:
-    c.add WhileKeyword
-    c.add ParLe
-    c.add "1"
-    c.add ParRi
-    c.add CurlyLe
-  of GotoLoop:
-    c.add CurlyRi
-  of Label:
-    let lab = t[n].label
-    c.add "L"
-    c.add $lab.int
-    c.add Colon
-    c.add Semicolon
-  of Goto:
-    let lab = t[n].label
-    c.add "goto L"
-    c.add $lab.int
-    c.add Semicolon
-  of CheckedGoto:
-    discard "XXX todo"
-  of ArrayConstr:
-    c.add CurlyLe
-    c.add ".a = "
-    c.add CurlyLe
-    var i = 0
-    for ch in sonsFrom1(t, n):
-      if i > 0: c.add Comma
-      c.gen t, ch
-      inc i
-    c.add CurlyRi
-    c.add CurlyRi
-  of ObjConstr:
-    c.add CurlyLe
-    var i = 0
-    for ch in sonsFrom1(t, n):
-      if i mod 2 == 0:
-        if i > 0: c.add Comma
-        c.add ".F" & $t[ch].immediateVal
-        c.add AsgnOpr
-      else:
-        c.gen t, ch
-      inc i
-    c.add CurlyRi
-  of Ret:
-    c.add ReturnKeyword
-    c.gen t, n.firstSon
-    c.add Semicolon
-  of Select:
-    c.add SwitchKeyword
-    c.add ParLe
-    let (_, selector) = sons2(t, n)
-    c.gen t, selector
-    c.add ParRi
-    c.add CurlyLe
-    for ch in sonsFromN(t, n, 2):
-      c.gen t, ch
-    c.add CurlyRi
-  of SelectPair:
-    let (le, ri) = sons2(t, n)
-    c.gen t, le
-    c.gen t, ri
-  of SelectList:
-    for ch in sons(t, n):
-      c.gen t, ch
-  of SelectValue:
-    c.add CaseKeyword
-    c.gen t, n.firstSon
-    c.add Colon
-  of SelectRange:
-    let (le, ri) = sons2(t, n)
-    c.add CaseKeyword
-    c.gen t, le
-    c.add " ... "
-    c.gen t, ri
-    c.add Colon
-  of ForeignDecl:
-    c.data.add LitId(ExternKeyword)
-    c.gen t, n.firstSon
-  of SummonGlobal:
-    moveToDataSection:
-      let (typ, sym) = sons2(t, n)
-      c.genGlobal t, sym, typ, ""
-      c.add Semicolon
-  of SummonThreadLocal:
-    moveToDataSection:
-      let (typ, sym) = sons2(t, n)
-      c.genGlobal t, sym, typ, "__thread "
-      c.add Semicolon
-  of SummonConst:
-    moveToDataSection:
-      let (typ, sym, val) = sons3(t, n)
-      c.genGlobal t, sym, typ, "const "
-      c.add AsgnOpr
-      c.gen t, val
-      c.add Semicolon
-  of Summon, SummonResult:
-    let (typ, sym) = sons2(t, n)
-    c.genLocal t, sym, typ, ""
-    c.add Semicolon
-
-  of SummonParam:
-    raiseAssert "SummonParam should have been handled in genProc"
-  of Kill:
-    discard "we don't care about Kill instructions"
-  of AddrOf:
-    let (_, arg) = sons2(t, n)
-    c.add "&"
-    gen c, t, arg
-  of DerefArrayAt:
-    let (_, a, i) = sons3(t, n)
-    gen c, t, a
-    c.add BracketLe
-    gen c, t, i
-    c.add BracketRi
-  of ArrayAt:
-    let (_, a, i) = sons3(t, n)
-    gen c, t, a
-    c.add Dot
-    c.add "a"
-    c.add BracketLe
-    gen c, t, i
-    c.add BracketRi
-  of FieldAt:
-    let (_, a, b) = sons3(t, n)
-    gen c, t, a
-    let field = t[b].immediateVal
-    c.add Dot
-    c.add "F" & $field
-  of DerefFieldAt:
-    let (_, a, b) = sons3(t, n)
-    gen c, t, a
-    let field = t[b].immediateVal
-    c.add Arrow
-    c.add "F" & $field
-  of Load:
-    let (_, arg) = sons2(t, n)
-    c.add ParLe
-    c.add "*"
-    gen c, t, arg
-    c.add ParRi
-  of Store:
-    raiseAssert "Assumption was that Store is unused!"
-  of Asgn:
-    let (_, dest, src) = sons3(t, n)
-    gen c, t, dest
-    c.add AsgnOpr
-    gen c, t, src
-    c.add Semicolon
-  of CheckedRange:
-    c.add "nimCheckRange"
-    c.add ParLe
-    let (_, gotoInstr, x, a, b) = sons5(t, n)
-    gen c, t, x
-    c.add Comma
-    gen c, t, a
-    c.add Comma
-    gen c, t, b
-    c.add Comma
-    c.add "L" & $t[gotoInstr].label.int
-    c.add ParRi
-  of CheckedIndex:
-    c.add "nimCheckIndex"
-    c.add ParLe
-    let (gotoInstr, x, a) = sons3(t, n)
-    gen c, t, x
-    c.add Comma
-    gen c, t, a
-    c.add Comma
-    c.add "L" & $t[gotoInstr].label.int
-    c.add ParRi
-  of Call, IndirectCall:
-    let (typ, fn) = sons2(t, n)
-    gen c, t, fn
-    c.add ParLe
-    var i = 0
-    for ch in sonsFromN(t, n, 2):
-      if i > 0: c.add Comma
-      gen c, t, ch
-      inc i
-    c.add ParRi
-    if c.m.types[t[typ].typeId].kind == VoidTy:
-      c.add Semicolon
-  of CheckedCall, CheckedIndirectCall:
-    let (typ, gotoInstr, fn) = sons3(t, n)
-    gen c, t, fn
-    c.add ParLe
-    var i = 0
-    for ch in sonsFromN(t, n, 3):
-      if i > 0: c.add Comma
-      gen c, t, ch
-      inc i
-    c.add ParRi
-    if c.m.types[t[typ].typeId].kind == VoidTy:
-      c.add Semicolon
-
-  of CheckedAdd: checkedBinaryop "nimAddInt"
-  of CheckedSub: checkedBinaryop "nimSubInt"
-  of CheckedMul: checkedBinaryop "nimMulInt"
-  of CheckedDiv: checkedBinaryop "nimDivInt"
-  of CheckedMod: checkedBinaryop "nimModInt"
-  of Add: triop " + "
-  of Sub: triop " - "
-  of Mul: triop " * "
-  of Div: triop " / "
-  of Mod: triop " % "
-  of BitShl: triop " << "
-  of BitShr: triop " >> "
-  of BitAnd: triop " & "
-  of BitOr: triop " | "
-  of BitXor: triop " ^ "
-  of BitNot: binaryop " ~ "
-  of BoolNot: binaryop " !"
-  of Eq: cmpop " == "
-  of Le: cmpop " <= "
-  of Lt: cmpop " < "
-  of Cast: binaryop ""
-  of NumberConv: genNumberConv c, t, n
-  of CheckedObjConv: binaryop ""
-  of ObjConv: binaryop ""
-  of Emit: raiseAssert "cannot interpret: Emit"
-  of ProcDecl: genProcDecl c, t, n, false
-  of ForeignProcDecl: genProcDecl c, t, n, true
-  of PragmaPair, PragmaId, TestOf, Yld, SetExc, TestExc:
-    c.add "cannot interpret: " & $t[n].kind
-
-const
-  Prelude = """
-/* GENERATED CODE. DO NOT EDIT. */
-
-#ifdef __cplusplus
-#define NB8 bool
-#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901)
-// see #13798: to avoid conflicts for code emitting `#include <stdbool.h>`
-#define NB8 _Bool
-#else
-typedef unsigned char NB8; // best effort
-#endif
-
-typedef unsigned char NC8;
-
-typedef float NF32;
-typedef double NF64;
-#if defined(__BORLANDC__) || defined(_MSC_VER)
-typedef signed char NI8;
-typedef signed short int NI16;
-typedef signed int NI32;
-typedef __int64 NI64;
-/* XXX: Float128? */
-typedef unsigned char NU8;
-typedef unsigned short int NU16;
-typedef unsigned int NU32;
-typedef unsigned __int64 NU64;
-#elif defined(HAVE_STDINT_H)
-#ifndef USE_NIM_NAMESPACE
-#  include <stdint.h>
-#endif
-typedef int8_t NI8;
-typedef int16_t NI16;
-typedef int32_t NI32;
-typedef int64_t NI64;
-typedef uint8_t NU8;
-typedef uint16_t NU16;
-typedef uint32_t NU32;
-typedef uint64_t NU64;
-#elif defined(HAVE_CSTDINT)
-#ifndef USE_NIM_NAMESPACE
-#  include <cstdint>
-#endif
-typedef std::int8_t NI8;
-typedef std::int16_t NI16;
-typedef std::int32_t NI32;
-typedef std::int64_t NI64;
-typedef std::uint8_t NU8;
-typedef std::uint16_t NU16;
-typedef std::uint32_t NU32;
-typedef std::uint64_t NU64;
-#else
-/* Unknown compiler/version, do our best */
-#ifdef __INT8_TYPE__
-typedef __INT8_TYPE__ NI8;
-#else
-typedef signed char NI8;
-#endif
-#ifdef __INT16_TYPE__
-typedef __INT16_TYPE__ NI16;
-#else
-typedef signed short int NI16;
-#endif
-#ifdef __INT32_TYPE__
-typedef __INT32_TYPE__ NI32;
-#else
-typedef signed int NI32;
-#endif
-#ifdef __INT64_TYPE__
-typedef __INT64_TYPE__ NI64;
-#else
-typedef long long int NI64;
-#endif
-/* XXX: Float128? */
-#ifdef __UINT8_TYPE__
-typedef __UINT8_TYPE__ NU8;
-#else
-typedef unsigned char NU8;
-#endif
-#ifdef __UINT16_TYPE__
-typedef __UINT16_TYPE__ NU16;
-#else
-typedef unsigned short int NU16;
-#endif
-#ifdef __UINT32_TYPE__
-typedef __UINT32_TYPE__ NU32;
-#else
-typedef unsigned int NU32;
-#endif
-#ifdef __UINT64_TYPE__
-typedef __UINT64_TYPE__ NU64;
-#else
-typedef unsigned long long int NU64;
-#endif
-#endif
-
-#ifdef NIM_INTBITS
-#  if NIM_INTBITS == 64
-typedef NI64 NI;
-typedef NU64 NU;
-#  elif NIM_INTBITS == 32
-typedef NI32 NI;
-typedef NU32 NU;
-#  elif NIM_INTBITS == 16
-typedef NI16 NI;
-typedef NU16 NU;
-#  elif NIM_INTBITS == 8
-typedef NI8 NI;
-typedef NU8 NU;
-#  else
-#    error "invalid bit width for int"
-#  endif
-#endif
-
-#define NIM_STRLIT_FLAG ((NU)(1) << ((NIM_INTBITS) - 2)) /* This has to be the same as system.strlitFlag! */
-
-#define nimAddInt64(a, b, L) ({long long int res; if(__builtin_saddll_overflow(a, b, &res)) goto L; res})
-#define nimSubInt64(a, b, L) ({long long int res; if(__builtin_ssubll_overflow(a, b, &res)) goto L; res})
-#define nimMulInt64(a, b, L) ({long long int res; if(__builtin_smulll_overflow(a, b, &res)) goto L; res})
-
-#define nimAddInt32(a, b, L) ({long int res; if(__builtin_sadd_overflow(a, b, &res)) goto L; res})
-#define nimSubInt32(a, b, L) ({long int res; if(__builtin_ssub_overflow(a, b, &res)) goto L; res})
-#define nimMulInt32(a, b, L) ({long int res; if(__builtin_smul_overflow(a, b, &res)) goto L; res})
-
-#define nimCheckRange(x, a, b, L) ({if (x < a || x > b) goto L; x})
-#define nimCheckIndex(x, a, L) ({if (x >= a) goto L; x})
-
-"""
-
-proc traverseCode(c: var GeneratedCode) =
-  const AllowedInToplevelC = {SummonConst, SummonGlobal, SummonThreadLocal,
-                              ProcDecl, ForeignDecl, ForeignProcDecl}
-  var i = NodePos(0)
-  while i.int < c.m.code.len:
-    let oldLen = c.code.len
-    let moveToInitSection = c.m.code[NodePos(i)].kind notin AllowedInToplevelC
-
-    gen c, c.m.code, NodePos(i)
-    next c.m.code, i
-
-    if moveToInitSection:
-      for i in oldLen ..< c.code.len:
-        c.init.add c.code[i]
-      setLen c.code, oldLen
-
-proc generateCode*(inp, outp: string) =
-  var c = initGeneratedCode(load(inp))
-
-  var co = TypeOrder()
-  traverseTypes(c.m.types, c.m.lit, co)
-
-  generateTypes(c, c.m.types, c.m.lit, co)
-  let typeDecls = move c.code
-
-  traverseCode c
-  var f = CppFile(f: open(outp, fmWrite))
-  f.write "#define NIM_INTBITS " & $c.m.intbits & "\n"
-  f.write Prelude
-  writeTokenSeq f, c.includes, c
-  writeTokenSeq f, typeDecls, c
-  writeTokenSeq f, c.data, c
-  writeTokenSeq f, c.protos, c
-  writeTokenSeq f, c.code, c
-  if c.init.len > 0:
-    f.write "void __attribute__((constructor)) init(void) {"
-    writeTokenSeq f, c.init, c
-    f.write "}\n\n"
-  f.f.close
diff --git a/compiler/nir/nir.nim b/compiler/nir/nir.nim
deleted file mode 100644
index 6f7077fb0..000000000
--- a/compiler/nir/nir.nim
+++ /dev/null
@@ -1,105 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2023 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Nim Intermediate Representation, designed to capture all of Nim's semantics without losing too much
-## precious information. Can easily be translated into C. And to JavaScript, hopefully.
-
-from std/os import addFileExt, `/`, createDir
-
-import std / assertions
-import ".." / [ast, modulegraphs, renderer, transf, options, msgs, lineinfos]
-import nirtypes, nirinsts, ast2ir, nirlineinfos, nirfiles, nirvm
-
-import ".." / ic / [rodfiles, bitabs]
-
-type
-  PCtx* = ref object of TPassContext
-    m: ModuleCon
-    c: ProcCon
-    oldErrorCount: int
-    bytecode: Bytecode
-
-proc newCtx*(module: PSym; g: ModuleGraph; idgen: IdGenerator): PCtx =
-  var lit = Literals()
-  var nirm = (ref NirModule)(types: initTypeGraph(lit), lit: lit)
-  var m = initModuleCon(g, g.config, idgen, module, nirm)
-  m.noModularity = true
-  PCtx(m: m, c: initProcCon(m, nil, g.config), idgen: idgen, bytecode: initBytecode(nirm))
-
-proc refresh*(c: PCtx; module: PSym; idgen: IdGenerator) =
-  #c.m = initModuleCon(c.m.graph, c.m.graph.config, idgen, module, c.m.nirm)
-  #c.m.noModularity = true
-  c.c = initProcCon(c.m, nil, c.m.graph.config)
-  c.idgen = idgen
-
-proc setupGlobalCtx*(module: PSym; graph: ModuleGraph; idgen: IdGenerator) =
-  if graph.repl.isNil:
-    graph.repl = newCtx(module, graph, idgen)
-    #registerAdditionalOps(PCtx graph.repl)
-  else:
-    refresh(PCtx graph.repl, module, idgen)
-
-proc setupNirReplGen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
-  setupGlobalCtx(module, graph, idgen)
-  result = PCtx graph.repl
-
-proc evalStmt(c: PCtx; n: PNode) =
-  let n = transformExpr(c.m.graph, c.idgen, c.m.module, n)
-  let pc = genStmt(c.c, n)
-  #var res = ""
-  #toString c.m.nirm.code, NodePos(pc), c.m.nirm.lit.strings, c.m.nirm.lit.numbers, c.m.symnames, res
-  #res.add "\n--------------------------\n"
-  #toString res, c.m.types.g
-  if pc.int < c.m.nirm.code.len:
-    c.bytecode.interactive = c.m.graph.interactive
-    execCode c.bytecode, c.m.nirm.code, pc
-  #echo res
-
-proc runCode*(c: PPassContext; n: PNode): PNode =
-  let c = PCtx(c)
-  # don't eval errornous code:
-  if c.oldErrorCount == c.m.graph.config.errorCounter:
-    evalStmt(c, n)
-    result = newNodeI(nkEmpty, n.info)
-  else:
-    result = n
-  c.oldErrorCount = c.m.graph.config.errorCounter
-
-type
-  NirPassContext* = ref object of TPassContext
-    m: ModuleCon
-    c: ProcCon
-
-proc openNirBackend*(g: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
-  var lit = Literals()
-  var nirm = (ref NirModule)(types: initTypeGraph(lit), lit: lit)
-  let m = initModuleCon(g, g.config, idgen, module, nirm)
-  NirPassContext(m: m, c: initProcCon(m, nil, g.config), idgen: idgen)
-
-proc gen(c: NirPassContext; n: PNode) =
-  let n = transformExpr(c.m.graph, c.idgen, c.m.module, n)
-  let pc = genStmt(c.c, n)
-
-proc nirBackend*(c: PPassContext; n: PNode): PNode =
-  gen(NirPassContext(c), n)
-  result = n
-
-proc closeNirBackend*(c: PPassContext; finalNode: PNode) =
-  discard nirBackend(c, finalNode)
-
-  let c = NirPassContext(c)
-  let nimcache = getNimcacheDir(c.c.config).string
-  createDir nimcache
-  let outp = nimcache / c.m.module.name.s.addFileExt("nir")
-  #c.m.nirm.code = move c.c.code
-  try:
-    store c.m.nirm[], outp
-    echo "created: ", outp
-  except IOError:
-    rawMessage(c.c.config, errFatal, "serialization failed: " & outp)
diff --git a/compiler/nir/nirc.nim b/compiler/nir/nirc.nim
deleted file mode 100644
index a2cf69988..000000000
--- a/compiler/nir/nirc.nim
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2023 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Nir Compiler.
-
-import ".." / ic / [bitabs, rodfiles]
-import nirinsts, nirtypes, nirlineinfos, nirfiles, cir
-
-proc view(filename: string) =
-  let m = load(filename)
-  var res = ""
-  allTreesToString m.code, m.lit.strings, m.lit.numbers, m.symnames, res
-  res.add "\n# TYPES\n"
-  nirtypes.toString res, m.types
-  echo res
-
-import std / [syncio, parseopt]
-
-proc writeHelp =
-  echo """Usage: nirc view|c <file.nir>"""
-  quit 0
-
-proc main =
-  var inp = ""
-  var cmd = ""
-  for kind, key, val in getopt():
-    case kind
-    of cmdArgument:
-      if cmd.len == 0: cmd = key
-      elif inp.len == 0: inp = key
-      else: quit "Error: too many arguments"
-    of cmdLongOption, cmdShortOption:
-      case key
-      of "help", "h": writeHelp()
-      of "version", "v": stdout.write "1.0\n"
-    of cmdEnd: discard
-  if inp.len == 0:
-    quit "Error: no input file specified"
-  case cmd
-  of "", "view":
-    view inp
-  of "c":
-    let outp = inp & ".c"
-    cir.generateCode inp, outp
-
-main()
diff --git a/compiler/nir/nirfiles.nim b/compiler/nir/nirfiles.nim
deleted file mode 100644
index cd5a79f06..000000000
--- a/compiler/nir/nirfiles.nim
+++ /dev/null
@@ -1,83 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2023 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-import ".." / ic / [bitabs, rodfiles]
-import nirinsts, nirtypes, nirlineinfos
-
-type
-  NirModule* = object
-    code*: Tree
-    man*: LineInfoManager
-    types*: TypeGraph
-    lit*: Literals
-    namespace*: LitId
-    intbits*: uint32
-    symnames*: SymNames
-
-proc load*(filename: string): NirModule =
-  let lit = Literals()
-  result = NirModule(lit: lit, types: initTypeGraph(lit))
-  var r = rodfiles.open(filename)
-  try:
-    r.loadHeader(nirCookie)
-    r.loadSection stringsSection
-    r.load result.lit.strings
-
-    r.loadSection numbersSection
-    r.load result.lit.numbers
-
-    r.loadSection bodiesSection
-    r.load result.code
-
-    r.loadSection typesSection
-    r.load result.types
-
-    r.loadSection sideChannelSection
-    r.load result.man
-
-    r.loadSection namespaceSection
-    r.loadPrim result.namespace
-    r.loadPrim result.intbits
-
-    r.loadSection symnamesSection
-    r.load result.symnames
-
-  finally:
-    r.close()
-
-proc store*(m: NirModule; outp: string) =
-  var r = rodfiles.create(outp)
-  try:
-    r.storeHeader(nirCookie)
-    r.storeSection stringsSection
-    r.store m.lit.strings
-
-    r.storeSection numbersSection
-    r.store m.lit.numbers
-
-    r.storeSection bodiesSection
-    r.store m.code
-
-    r.storeSection typesSection
-    r.store m.types
-
-    r.storeSection sideChannelSection
-    r.store m.man
-
-    r.storeSection namespaceSection
-    r.storePrim m.namespace
-    r.storePrim m.intbits
-
-    r.storeSection symnamesSection
-    r.store m.symnames
-
-  finally:
-    r.close()
-  if r.err != ok:
-    raise newException(IOError, "could store into: " & outp)
diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim
deleted file mode 100644
index 6cffc1a89..000000000
--- a/compiler/nir/nirinsts.nim
+++ /dev/null
@@ -1,582 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2023 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## NIR instructions. Somewhat inspired by LLVM's instructions.
-
-import std / [assertions, hashes]
-import .. / ic / [bitabs, rodfiles]
-import nirlineinfos, nirtypes
-
-const
-  NirVersion = 1
-  nirCookie* = [byte(0), byte('N'), byte('I'), byte('R'),
-            byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(NirVersion)]
-
-type
-  SymId* = distinct int
-
-proc `$`*(s: SymId): string {.borrow.}
-proc hash*(s: SymId): Hash {.borrow.}
-proc `==`*(a, b: SymId): bool {.borrow.}
-
-type
-  Opcode* = enum
-    Nop,
-    ImmediateVal,
-    IntVal,
-    StrVal,
-    SymDef,
-    SymUse,
-    Typed,   # with type ID
-    PragmaId, # with Pragma ID, possible values: see PragmaKey enum
-    NilVal,
-    Label,
-    Goto,
-    CheckedGoto,
-    LoopLabel,
-    GotoLoop,  # last atom
-
-    ModuleSymUse, # `"module".x`
-
-    ArrayConstr,
-    ObjConstr,
-    Ret,
-    Yld,
-
-    Select,
-    SelectPair,  # ((values...), Label)
-    SelectList,  # (values...)
-    SelectValue, # (value)
-    SelectRange, # (valueA..valueB)
-    ForeignDecl, # Can wrap SummonGlobal, SummonThreadLocal, SummonConst
-    SummonGlobal,
-    SummonThreadLocal,
-    Summon, # x = Summon Typed <Type ID>; x begins to live
-    SummonResult,
-    SummonParam,
-    SummonConst,
-    Kill, # `Kill x`: scope end for `x`
-
-    AddrOf,
-    ArrayAt, # a[i]
-    DerefArrayAt, # a[i] where `a` is a PtrArray; `a[][i]`
-    FieldAt, # obj.field
-    DerefFieldAt, # obj[].field
-
-    Load, # a[]
-    Store, # a[] = b
-    Asgn,  # a = b
-    SetExc,
-    TestExc,
-
-    CheckedRange,
-    CheckedIndex,
-
-    Call,
-    IndirectCall,
-    CheckedCall, # call that can raise
-    CheckedIndirectCall, # call that can raise
-    CheckedAdd, # with overflow checking etc.
-    CheckedSub,
-    CheckedMul,
-    CheckedDiv,
-    CheckedMod,
-    Add,
-    Sub,
-    Mul,
-    Div,
-    Mod,
-    BitShl,
-    BitShr,
-    BitAnd,
-    BitOr,
-    BitXor,
-    BitNot,
-    BoolNot,
-    Eq,
-    Le,
-    Lt,
-    Cast,
-    NumberConv,
-    CheckedObjConv,
-    ObjConv,
-    TestOf,
-    Emit,
-    ProcDecl,
-    ForeignProcDecl,
-    PragmaPair
-
-type
-  PragmaKey* = enum
-    FastCall, StdCall, CDeclCall, SafeCall, SysCall, InlineCall, NoinlineCall, ThisCall, NoCall,
-    CoreName,
-    ExternName,
-    HeaderImport,
-    DllImport,
-    DllExport,
-    ObjExport
-
-const
-  LastAtomicValue = GotoLoop
-
-  OpcodeBits = 8'u32
-  OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32
-
-  ValueProducingAtoms = {ImmediateVal, IntVal, StrVal, SymUse, NilVal}
-
-  ValueProducing* = {
-    ImmediateVal,
-    IntVal,
-    StrVal,
-    SymUse,
-    NilVal,
-    ModuleSymUse,
-    ArrayConstr,
-    ObjConstr,
-    CheckedAdd,
-    CheckedSub,
-    CheckedMul,
-    CheckedDiv,
-    CheckedMod,
-    Add,
-    Sub,
-    Mul,
-    Div,
-    Mod,
-    BitShl,
-    BitShr,
-    BitAnd,
-    BitOr,
-    BitXor,
-    BitNot,
-    BoolNot,
-    Eq,
-    Le,
-    Lt,
-    Cast,
-    NumberConv,
-    CheckedObjConv,
-    ObjConv,
-    AddrOf,
-    Load,
-    ArrayAt,
-    DerefArrayAt,
-    FieldAt,
-    DerefFieldAt,
-    TestOf
-  }
-
-type
-  Instr* = object     # 8 bytes
-    x: uint32
-    info*: PackedLineInfo
-
-template kind*(n: Instr): Opcode = Opcode(n.x and OpcodeMask)
-template operand(n: Instr): uint32 = (n.x shr OpcodeBits)
-
-template rawOperand*(n: Instr): uint32 = (n.x shr OpcodeBits)
-
-template toX(k: Opcode; operand: uint32): uint32 =
-  uint32(k) or (operand shl OpcodeBits)
-
-template toX(k: Opcode; operand: LitId): uint32 =
-  uint32(k) or (operand.uint32 shl OpcodeBits)
-
-type
-  Tree* = object
-    nodes: seq[Instr]
-
-  Values* = object
-    numbers: BiTable[int64]
-    strings: BiTable[string]
-
-type
-  PatchPos* = distinct int
-  NodePos* = distinct int
-
-const
-  InvalidPatchPos* = PatchPos(-1)
-
-proc isValid(p: PatchPos): bool {.inline.} = p.int != -1
-
-proc prepare*(tree: var Tree; info: PackedLineInfo; kind: Opcode): PatchPos =
-  result = PatchPos tree.nodes.len
-  tree.nodes.add Instr(x: toX(kind, 1'u32), info: info)
-
-proc isAtom(tree: Tree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue
-proc isAtom(tree: Tree; pos: NodePos): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue
-
-proc patch*(tree: var Tree; pos: PatchPos) =
-  let pos = pos.int
-  let k = tree.nodes[pos].kind
-  assert k > LastAtomicValue
-  let distance = int32(tree.nodes.len - pos)
-  assert distance > 0
-  tree.nodes[pos].x = toX(k, cast[uint32](distance))
-
-template build*(tree: var Tree; info: PackedLineInfo; kind: Opcode; body: untyped) =
-  let pos = prepare(tree, info, kind)
-  body
-  patch(tree, pos)
-
-template buildTyped*(tree: var Tree; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) =
-  let pos = prepare(tree, info, kind)
-  tree.addTyped info, typ
-  body
-  patch(tree, pos)
-
-proc len*(tree: Tree): int {.inline.} = tree.nodes.len
-
-template rawSpan(n: Instr): int = int(operand(n))
-
-proc nextChild(tree: Tree; pos: var int) {.inline.} =
-  if tree.nodes[pos].kind > LastAtomicValue:
-    assert tree.nodes[pos].operand > 0'u32
-    inc pos, tree.nodes[pos].rawSpan
-  else:
-    inc pos
-
-proc next*(tree: Tree; pos: var NodePos) {.inline.} = nextChild tree, int(pos)
-
-template firstSon*(n: NodePos): NodePos = NodePos(n.int+1)
-
-template skipTyped*(n: NodePos): NodePos = NodePos(n.int+2)
-
-iterator sons*(tree: Tree; n: NodePos): NodePos =
-  var pos = n.int
-  assert tree.nodes[pos].kind > LastAtomicValue
-  let last = pos + tree.nodes[pos].rawSpan
-  inc pos
-  while pos < last:
-    yield NodePos pos
-    nextChild tree, pos
-
-iterator sonsFrom1*(tree: Tree; n: NodePos): NodePos =
-  var pos = n.int
-  assert tree.nodes[pos].kind > LastAtomicValue
-  let last = pos + tree.nodes[pos].rawSpan
-  inc pos
-  nextChild tree, pos
-  while pos < last:
-    yield NodePos pos
-    nextChild tree, pos
-
-iterator sonsFromN*(tree: Tree; n: NodePos; toSkip = 2): NodePos =
-  var pos = n.int
-  assert tree.nodes[pos].kind > LastAtomicValue
-  let last = pos + tree.nodes[pos].rawSpan
-  inc pos
-  for i in 1..toSkip:
-    nextChild tree, pos
-  while pos < last:
-    yield NodePos pos
-    nextChild tree, pos
-
-template `[]`*(t: Tree; n: NodePos): Instr = t.nodes[n.int]
-
-iterator sonsRest*(tree: Tree; parent, n: NodePos): NodePos =
-  var pos = n.int
-  assert tree[parent].kind > LastAtomicValue
-  let last = parent.int + tree[parent].rawSpan
-  while pos < last:
-    yield NodePos pos
-    nextChild tree, pos
-
-proc span(tree: Tree; pos: int): int {.inline.} =
-  if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand)
-
-proc copyTree*(dest: var Tree; src: Tree) =
-  let pos = 0
-  let L = span(src, pos)
-  let d = dest.nodes.len
-  dest.nodes.setLen(d + L)
-  assert L > 0
-  for i in 0..<L:
-    dest.nodes[d+i] = src.nodes[pos+i]
-
-proc sons2*(tree: Tree; n: NodePos): (NodePos, NodePos) {.inline.} =
-  assert(not isAtom(tree, n.int))
-  let a = n.int+1
-  let b = a + span(tree, a)
-  result = (NodePos a, NodePos b)
-
-proc sons3*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos) {.inline.} =
-  assert(not isAtom(tree, n.int))
-  let a = n.int+1
-  let b = a + span(tree, a)
-  let c = b + span(tree, b)
-  result = (NodePos a, NodePos b, NodePos c)
-
-proc sons4*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos, NodePos) {.inline.} =
-  assert(not isAtom(tree, n.int))
-  let a = n.int+1
-  let b = a + span(tree, a)
-  let c = b + span(tree, b)
-  let d = c + span(tree, c)
-  result = (NodePos a, NodePos b, NodePos c, NodePos d)
-
-proc sons5*(tree: Tree; n: NodePos): (NodePos, NodePos, NodePos, NodePos, NodePos) {.inline.} =
-  assert(not isAtom(tree, n.int))
-  let a = n.int+1
-  let b = a + span(tree, a)
-  let c = b + span(tree, b)
-  let d = c + span(tree, c)
-  let e = d + span(tree, d)
-  result = (NodePos a, NodePos b, NodePos c, NodePos d, NodePos e)
-
-proc typeId*(ins: Instr): TypeId {.inline.} =
-  assert ins.kind == Typed
-  result = TypeId(ins.operand)
-
-proc symId*(ins: Instr): SymId {.inline.} =
-  assert ins.kind in {SymUse, SymDef}
-  result = SymId(ins.operand)
-
-proc immediateVal*(ins: Instr): int {.inline.} =
-  assert ins.kind == ImmediateVal
-  result = cast[int](ins.operand)
-
-proc litId*(ins: Instr): LitId {.inline.} =
-  assert ins.kind in {StrVal, IntVal}
-  result = LitId(ins.operand)
-
-
-type
-  LabelId* = distinct int
-
-proc `==`*(a, b: LabelId): bool {.borrow.}
-proc hash*(a: LabelId): Hash {.borrow.}
-
-proc label*(ins: Instr): LabelId {.inline.} =
-  assert ins.kind in {Label, LoopLabel, Goto, GotoLoop, CheckedGoto}
-  result = LabelId(ins.operand)
-
-proc newLabel*(labelGen: var int): LabelId {.inline.} =
-  result = LabelId labelGen
-  inc labelGen
-
-proc newLabels*(labelGen: var int; n: int): LabelId {.inline.} =
-  result = LabelId labelGen
-  inc labelGen, n
-
-proc addNewLabel*(t: var Tree; labelGen: var int; info: PackedLineInfo; k: Opcode): LabelId =
-  assert k in {Label, LoopLabel}
-  result = LabelId labelGen
-  t.nodes.add Instr(x: toX(k, uint32(result)), info: info)
-  inc labelGen
-
-proc gotoLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) =
-  assert k in {Goto, GotoLoop, CheckedGoto}
-  t.nodes.add Instr(x: toX(k, uint32(L)), info: info)
-
-proc addLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) {.inline.} =
-  assert k in {Label, LoopLabel, Goto, GotoLoop, CheckedGoto}
-  t.nodes.add Instr(x: toX(k, uint32(L)), info: info)
-
-proc addSymUse*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
-  t.nodes.add Instr(x: toX(SymUse, uint32(s)), info: info)
-
-proc addSymDef*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} =
-  t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info)
-
-proc addNop*(t: var Tree; info: PackedLineInfo) {.inline.} =
-  t.nodes.add Instr(x: toX(Nop, 0'u32), info: info)
-
-proc addTyped*(t: var Tree; info: PackedLineInfo; typ: TypeId) {.inline.} =
-  assert typ.int >= 0
-  t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info)
-
-proc addSummon*(t: var Tree; info: PackedLineInfo; s: SymId; typ: TypeId; opc = Summon) {.inline.} =
-  assert typ.int >= 0
-  assert opc in {Summon, SummonConst, SummonGlobal, SummonThreadLocal, SummonParam, SummonResult}
-  let x = prepare(t, info, opc)
-  t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info)
-  t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info)
-  patch t, x
-
-proc addImmediateVal*(t: var Tree; info: PackedLineInfo; x: int) =
-  assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int)
-  t.nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info)
-
-proc addPragmaId*(t: var Tree; info: PackedLineInfo; x: PragmaKey) =
-  t.nodes.add Instr(x: toX(PragmaId, uint32(x)), info: info)
-
-proc addIntVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) =
-  buildTyped t, info, NumberConv, typ:
-    t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(x))), info: info)
-
-proc boolVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; b: bool) =
-  buildTyped t, info, NumberConv, Bool8Id:
-    t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(ord b))), info: info)
-
-proc addStrVal*(t: var Tree; strings: var BiTable[string]; info: PackedLineInfo; s: string) =
-  t.nodes.add Instr(x: toX(StrVal, uint32(strings.getOrIncl(s))), info: info)
-
-proc addStrLit*(t: var Tree; info: PackedLineInfo; s: LitId) =
-  t.nodes.add Instr(x: toX(StrVal, uint32(s)), info: info)
-
-proc addNilVal*(t: var Tree; info: PackedLineInfo; typ: TypeId) =
-  buildTyped t, info, NumberConv, typ:
-    t.nodes.add Instr(x: toX(NilVal, uint32(0)), info: info)
-
-proc store*(r: var RodFile; t: Tree) = storeSeq r, t.nodes
-proc load*(r: var RodFile; t: var Tree) = loadSeq r, t.nodes
-
-proc escapeToNimLit*(s: string; result: var string) =
-  result.add '"'
-  for c in items s:
-    if c < ' ' or int(c) >= 128:
-      result.add '\\'
-      result.addInt int(c)
-    elif c == '\\':
-      result.add r"\\"
-    elif c == '\n':
-      result.add r"\n"
-    elif c == '\r':
-      result.add r"\r"
-    elif c == '\t':
-      result.add r"\t"
-    else:
-      result.add c
-  result.add '"'
-
-type
-  SymNames* = object
-    s: seq[LitId]
-
-proc `[]=`*(t: var SymNames; key: SymId; val: LitId) =
-  let k = int(key)
-  if k >= t.s.len: t.s.setLen k+1
-  t.s[k] = val
-
-proc `[]`*(t: SymNames; key: SymId): LitId =
-  let k = int(key)
-  if k < t.s.len: result = t.s[k]
-  else: result = LitId(0)
-
-template localName(s: SymId): string =
-  let name = names[s]
-  if name != LitId(0):
-    strings[name]
-  else:
-    $s.int
-
-proc store*(r: var RodFile; t: SymNames) = storeSeq(r, t.s)
-proc load*(r: var RodFile; t: var SymNames) = loadSeq(r, t.s)
-
-proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTable[int64];
-               names: SymNames;
-               r: var string; nesting = 0) =
-  if r.len > 0 and r[r.len-1] notin {' ', '\n', '(', '[', '{'}:
-    r.add ' '
-
-  case t[pos].kind
-  of Nop: r.add "Nop"
-  of ImmediateVal:
-    r.add $t[pos].operand
-  of IntVal:
-    r.add "IntVal "
-    r.add $integers[LitId t[pos].operand]
-  of StrVal:
-    escapeToNimLit(strings[LitId t[pos].operand], r)
-  of SymDef:
-    r.add "SymDef "
-    r.add localName(SymId t[pos].operand)
-  of SymUse:
-    r.add "SymUse "
-    r.add localName(SymId t[pos].operand)
-  of PragmaId:
-    r.add $cast[PragmaKey](t[pos].operand)
-  of Typed:
-    r.add "T<"
-    r.add $t[pos].operand
-    r.add ">"
-  of NilVal:
-    r.add "NilVal"
-  of Label:
-    # undo the nesting:
-    var spaces = r.len-1
-    while spaces >= 0 and r[spaces] == ' ': dec spaces
-    r.setLen spaces+1
-    r.add "\n  L"
-    r.add $t[pos].operand
-  of Goto, CheckedGoto, LoopLabel, GotoLoop:
-    r.add $t[pos].kind
-    r.add " L"
-    r.add $t[pos].operand
-  else:
-    r.add $t[pos].kind
-    r.add "{\n"
-    for i in 0..<(nesting+1)*2: r.add ' '
-    for p in sons(t, pos):
-      toString t, p, strings, integers, names, r, nesting+1
-    r.add "\n"
-    for i in 0..<nesting*2: r.add ' '
-    r.add "}"
-
-proc allTreesToString*(t: Tree; strings: BiTable[string]; integers: BiTable[int64];
-                       names: SymNames;
-                       r: var string) =
-  var i = 0
-  while i < t.len:
-    toString t, NodePos(i), strings, integers, names, r
-    nextChild t, i
-
-type
-  Value* = distinct Tree
-
-proc prepare*(dest: var Value; info: PackedLineInfo; k: Opcode): PatchPos {.inline.} =
-  assert k in ValueProducing - ValueProducingAtoms
-  result = prepare(Tree(dest), info, k)
-
-proc patch*(dest: var Value; pos: PatchPos) {.inline.} =
-  patch(Tree(dest), pos)
-
-proc localToValue*(info: PackedLineInfo; s: SymId): Value =
-  result = Value(Tree())
-  Tree(result).addSymUse info, s
-
-proc hasValue*(v: Value): bool {.inline.} = Tree(v).len > 0
-
-proc isEmpty*(v: Value): bool {.inline.} = Tree(v).len == 0
-
-proc extractTemp*(v: Value): SymId =
-  if hasValue(v) and Tree(v)[NodePos 0].kind == SymUse:
-    result = SymId(Tree(v)[NodePos 0].operand)
-  else:
-    result = SymId(-1)
-
-proc copyTree*(dest: var Tree; src: Value) = copyTree dest, Tree(src)
-
-proc addImmediateVal*(t: var Value; info: PackedLineInfo; x: int) =
-  assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int)
-  Tree(t).nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info)
-
-template build*(tree: var Value; info: PackedLineInfo; kind: Opcode; body: untyped) =
-  let pos = prepare(Tree(tree), info, kind)
-  body
-  patch(tree, pos)
-
-proc addTyped*(t: var Value; info: PackedLineInfo; typ: TypeId) {.inline.} =
-  addTyped(Tree(t), info, typ)
-
-template buildTyped*(tree: var Value; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) =
-  let pos = prepare(tree, info, kind)
-  tree.addTyped info, typ
-  body
-  patch(tree, pos)
-
-proc addStrVal*(t: var Value; strings: var BiTable[string]; info: PackedLineInfo; s: string) =
-  addStrVal(Tree(t), strings, info, s)
-
-proc addNilVal*(t: var Value; info: PackedLineInfo; typ: TypeId) =
-  addNilVal Tree(t), info, typ
-
-proc addIntVal*(t: var Value; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) =
-  addIntVal Tree(t), integers, info, typ, x
diff --git a/compiler/nir/nirslots.nim b/compiler/nir/nirslots.nim
deleted file mode 100644
index a01e7a633..000000000
--- a/compiler/nir/nirslots.nim
+++ /dev/null
@@ -1,104 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2023 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Management of slots. Similar to "register allocation"
-## in lower level languages.
-
-import std / [assertions, tables]
-import nirtypes, nirinsts
-
-type
-  SlotManagerFlag* = enum
-    ReuseTemps,
-    ReuseVars
-  SlotKind* = enum
-    Temp, Perm
-  SlotManager* = object # "register allocator"
-    live: Table[SymId, (SlotKind, TypeId)]
-    dead: Table[TypeId, seq[SymId]]
-    flags: set[SlotManagerFlag]
-    inScope: seq[SymId]
-
-proc initSlotManager*(flags: set[SlotManagerFlag]): SlotManager {.inline.} =
-  SlotManager(flags: flags)
-
-proc allocRaw(m: var SlotManager; t: TypeId; f: SlotManagerFlag; k: SlotKind;
-              symIdgen: var int32): SymId {.inline.} =
-  if f in m.flags and m.dead.hasKey(t) and m.dead[t].len > 0:
-    result = m.dead[t].pop()
-  else:
-    inc symIdgen
-    result = SymId(symIdgen)
-    m.inScope.add result
-  m.live[result] = (k, t)
-
-proc allocTemp*(m: var SlotManager; t: TypeId; symIdgen: var int32): SymId {.inline.} =
-  result = allocRaw(m, t, ReuseTemps, Temp, symIdgen)
-
-proc allocVar*(m: var SlotManager; t: TypeId; symIdgen: var int32): SymId {.inline.} =
-  result = allocRaw(m, t, ReuseVars, Perm, symIdgen)
-
-proc freeLoc*(m: var SlotManager; s: SymId) =
-  let t = m.live.getOrDefault(s)
-  assert t[1].int != 0
-  m.live.del s
-  m.dead.mgetOrPut(t[1], @[]).add s
-
-proc freeTemp*(m: var SlotManager; s: SymId) =
-  let t = m.live.getOrDefault(s)
-  if t[1].int != 0 and t[0] == Temp:
-    m.live.del s
-    m.dead.mgetOrPut(t[1], @[]).add s
-
-iterator stillAlive*(m: SlotManager): (SymId, TypeId) =
-  for k, v in pairs(m.live):
-    yield (k, v[1])
-
-proc getType*(m: SlotManager; s: SymId): TypeId {.inline.} = m.live[s][1]
-
-proc openScope*(m: var SlotManager) =
-  m.inScope.add SymId(-1) # add marker
-
-proc closeScope*(m: var SlotManager) =
-  var i = m.inScope.len - 1
-  while i >= 0:
-    if m.inScope[i] == SymId(-1):
-      m.inScope.setLen i
-      break
-    dec i
-
-when isMainModule:
-  var symIdgen: int32
-  var m = initSlotManager({ReuseTemps})
-
-  var g = initTypeGraph(Literals())
-
-  let a = g.openType ArrayTy
-  g.addBuiltinType Int8Id
-  g.addArrayLen 5
-  let finalArrayType = finishType(g, a)
-
-  let obj = g.openType ObjectDecl
-  g.addName "MyType"
-
-  g.addField "p", finalArrayType, 0
-  let objB = finishType(g, obj)
-
-  let x = m.allocTemp(objB, symIdgen)
-  assert x.int == 0
-
-  let y = m.allocTemp(objB, symIdgen)
-  assert y.int == 1
-
-  let z = m.allocTemp(Int8Id, symIdgen)
-  assert z.int == 2
-
-  m.freeLoc y
-  let y2 = m.allocTemp(objB, symIdgen)
-  assert y2.int == 1
diff --git a/compiler/nir/nirtypes.nim b/compiler/nir/nirtypes.nim
deleted file mode 100644
index a79bf6d01..000000000
--- a/compiler/nir/nirtypes.nim
+++ /dev/null
@@ -1,475 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2023 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Type system for NIR. Close to C's type system but without its quirks.
-
-import std / [assertions, hashes]
-import .. / ic / [bitabs, rodfiles]
-
-type
-  NirTypeKind* = enum
-    VoidTy, IntTy, UIntTy, FloatTy, BoolTy, CharTy, NameVal,
-    IntVal, SizeVal, AlignVal, OffsetVal,
-    AnnotationVal,
-    ObjectTy,
-    UnionTy,
-    VarargsTy, # the `...` in a C prototype; also the last "atom"
-    APtrTy, # pointer to aliasable memory
-    UPtrTy, # pointer to unique/unaliasable memory
-    AArrayPtrTy, # pointer to array of aliasable memory
-    UArrayPtrTy, # pointer to array of unique/unaliasable memory
-    ArrayTy,
-    LastArrayTy, # array of unspecified size as a last field inside an object
-    ProcTy,
-    ObjectDecl,
-    UnionDecl,
-    FieldDecl
-
-const
-  TypeKindBits = 8'u32
-  TypeKindMask = (1'u32 shl TypeKindBits) - 1'u32
-
-type
-  TypeNode* = object     # 4 bytes
-    x: uint32
-
-template kind*(n: TypeNode): NirTypeKind = NirTypeKind(n.x and TypeKindMask)
-template operand(n: TypeNode): uint32 = (n.x shr TypeKindBits)
-
-proc integralBits*(n: TypeNode): int {.inline.} =
-  # Number of bits in the IntTy, etc. Only valid for integral types.
-  assert n.kind in {IntTy, UIntTy, FloatTy, BoolTy, CharTy}
-  result = int(n.operand)
-
-template toX(k: NirTypeKind; operand: uint32): uint32 =
-  uint32(k) or (operand shl TypeKindBits)
-
-template toX(k: NirTypeKind; operand: LitId): uint32 =
-  uint32(k) or (operand.uint32 shl TypeKindBits)
-
-type
-  TypeId* = distinct int
-
-proc `==`*(a, b: TypeId): bool {.borrow.}
-proc hash*(a: TypeId): Hash {.borrow.}
-
-type
-  Literals* = ref object
-    strings*: BiTable[string]
-    numbers*: BiTable[int64]
-
-  TypeGraph* = object
-    nodes: seq[TypeNode]
-    lit: Literals
-
-const
-  VoidId* = TypeId 0
-  Bool8Id* = TypeId 1
-  Char8Id* = TypeId 2
-  Int8Id* = TypeId 3
-  Int16Id* = TypeId 4
-  Int32Id* = TypeId 5
-  Int64Id* = TypeId 6
-  UInt8Id* = TypeId 7
-  UInt16Id* = TypeId 8
-  UInt32Id* = TypeId 9
-  UInt64Id* = TypeId 10
-  Float32Id* = TypeId 11
-  Float64Id* = TypeId 12
-  VoidPtrId* = TypeId 13
-  LastBuiltinId* = 13
-
-proc initTypeGraph*(lit: Literals): TypeGraph =
-  result = TypeGraph(nodes: @[
-    TypeNode(x: toX(VoidTy, 0'u32)),
-    TypeNode(x: toX(BoolTy, 8'u32)),
-    TypeNode(x: toX(CharTy, 8'u32)),
-    TypeNode(x: toX(IntTy, 8'u32)),
-    TypeNode(x: toX(IntTy, 16'u32)),
-    TypeNode(x: toX(IntTy, 32'u32)),
-    TypeNode(x: toX(IntTy, 64'u32)),
-    TypeNode(x: toX(UIntTy, 8'u32)),
-    TypeNode(x: toX(UIntTy, 16'u32)),
-    TypeNode(x: toX(UIntTy, 32'u32)),
-    TypeNode(x: toX(UIntTy, 64'u32)),
-    TypeNode(x: toX(FloatTy, 32'u32)),
-    TypeNode(x: toX(FloatTy, 64'u32)),
-    TypeNode(x: toX(APtrTy, 2'u32)),
-    TypeNode(x: toX(VoidTy, 0'u32))
-  ], lit: lit)
-  assert result.nodes.len == LastBuiltinId+2
-
-type
-  TypePatchPos* = distinct int
-
-const
-  InvalidTypePatchPos* = TypePatchPos(-1)
-  LastAtomicValue = VarargsTy
-
-proc isValid(p: TypePatchPos): bool {.inline.} = p.int != -1
-
-proc prepare(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos =
-  result = TypePatchPos tree.nodes.len
-  tree.nodes.add TypeNode(x: toX(kind, 1'u32))
-
-proc isAtom(tree: TypeGraph; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue
-proc isAtom(tree: TypeGraph; pos: TypeId): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue
-
-proc patch(tree: var TypeGraph; pos: TypePatchPos) =
-  let pos = pos.int
-  let k = tree.nodes[pos].kind
-  assert k > LastAtomicValue
-  let distance = int32(tree.nodes.len - pos)
-  assert distance > 0
-  tree.nodes[pos].x = toX(k, cast[uint32](distance))
-
-proc len*(tree: TypeGraph): int {.inline.} = tree.nodes.len
-
-template rawSpan(n: TypeNode): int = int(operand(n))
-
-proc nextChild(tree: TypeGraph; pos: var int) {.inline.} =
-  if tree.nodes[pos].kind > LastAtomicValue:
-    assert tree.nodes[pos].operand > 0'u32
-    inc pos, tree.nodes[pos].rawSpan
-  else:
-    inc pos
-
-iterator sons*(tree: TypeGraph; n: TypeId): TypeId =
-  var pos = n.int
-  assert tree.nodes[pos].kind > LastAtomicValue
-  let last = pos + tree.nodes[pos].rawSpan
-  inc pos
-  while pos < last:
-    yield TypeId pos
-    nextChild tree, pos
-
-template `[]`*(t: TypeGraph; n: TypeId): TypeNode = t.nodes[n.int]
-
-proc elementType*(tree: TypeGraph; n: TypeId): TypeId {.inline.} =
-  assert tree[n].kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, ArrayTy, LastArrayTy}
-  result = TypeId(n.int+1)
-
-proc litId*(n: TypeNode): LitId {.inline.} =
-  assert n.kind in {NameVal, IntVal, SizeVal, AlignVal, OffsetVal, AnnotationVal, ObjectTy, UnionTy}
-  result = LitId(n.operand)
-
-proc kind*(tree: TypeGraph; n: TypeId): NirTypeKind {.inline.} = tree[n].kind
-
-proc span(tree: TypeGraph; pos: int): int {.inline.} =
-  if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand)
-
-proc sons2(tree: TypeGraph; n: TypeId): (TypeId, TypeId) =
-  assert(not isAtom(tree, n.int))
-  let a = n.int+1
-  let b = a + span(tree, a)
-  result = (TypeId a, TypeId b)
-
-proc sons3(tree: TypeGraph; n: TypeId): (TypeId, TypeId, TypeId) =
-  assert(not isAtom(tree, n.int))
-  let a = n.int+1
-  let b = a + span(tree, a)
-  let c = b + span(tree, b)
-  result = (TypeId a, TypeId b, TypeId c)
-
-proc arrayName*(tree: TypeGraph; n: TypeId): TypeId {.inline.} =
-  assert tree[n].kind == ArrayTy
-  let (_, _, c) = sons3(tree, n)
-  result = c
-
-proc arrayLen*(tree: TypeGraph; n: TypeId): BiggestInt =
-  assert tree[n].kind == ArrayTy
-  let (_, b) = sons2(tree, n)
-  result = tree.lit.numbers[LitId tree[b].operand]
-
-proc returnType*(tree: TypeGraph; n: TypeId): (TypeId, TypeId) =
-  # Returns the positions of the return type + calling convention.
-  var pos = n.int
-  assert tree.nodes[pos].kind == ProcTy
-  let a = n.int+1
-  let b = a + span(tree, a)
-  result = (TypeId b, TypeId a) # not a typo, order is reversed
-
-iterator params*(tree: TypeGraph; n: TypeId): TypeId =
-  var pos = n.int
-  assert tree.nodes[pos].kind == ProcTy
-  let last = pos + tree.nodes[pos].rawSpan
-  inc pos
-  nextChild tree, pos
-  nextChild tree, pos
-  while pos < last:
-    yield TypeId pos
-    nextChild tree, pos
-
-proc openType*(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos =
-  assert kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy,
-    ArrayTy, LastArrayTy, ProcTy, ObjectDecl, UnionDecl,
-    FieldDecl}
-  result = prepare(tree, kind)
-
-template typeInvariant(p: TypePatchPos) =
-  when false:
-    if tree[TypeId(p)].kind == FieldDecl:
-      var k = 0
-      for ch in sons(tree, TypeId(p)):
-        inc k
-      assert k > 2, "damn! " & $k
-
-proc sealType*(tree: var TypeGraph; p: TypePatchPos) =
-  patch tree, p
-  typeInvariant(p)
-
-proc finishType*(tree: var TypeGraph; p: TypePatchPos): TypeId =
-  # Search for an existing instance of this type in
-  # order to reduce memory consumption:
-  patch tree, p
-  typeInvariant(p)
-
-  let s = span(tree, p.int)
-  var i = 0
-  while i < p.int:
-    if tree.nodes[i].x == tree.nodes[p.int].x:
-      var isMatch = true
-      for j in 1..<s:
-        if tree.nodes[j+i].x == tree.nodes[j+p.int].x:
-          discard "still a match"
-        else:
-          isMatch = false
-          break
-      if isMatch:
-        if p.int+s == tree.len:
-          setLen tree.nodes, p.int
-        return TypeId(i)
-    nextChild tree, i
-  result = TypeId(p)
-
-proc nominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string): TypeId =
-  assert kind in {ObjectTy, UnionTy}
-  let content = TypeNode(x: toX(kind, tree.lit.strings.getOrIncl(name)))
-  for i in 0..<tree.len:
-    if tree.nodes[i].x == content.x:
-      return TypeId(i)
-  result = TypeId tree.nodes.len
-  tree.nodes.add content
-
-proc addNominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string) =
-  assert kind in {ObjectTy, UnionTy}
-  tree.nodes.add TypeNode(x: toX(kind, tree.lit.strings.getOrIncl(name)))
-
-proc getTypeTag*(tree: TypeGraph; t: TypeId): string =
-  assert tree[t].kind in {ObjectTy, UnionTy}
-  result = tree.lit.strings[LitId tree[t].operand]
-
-proc addVarargs*(tree: var TypeGraph) =
-  tree.nodes.add TypeNode(x: toX(VarargsTy, 0'u32))
-
-proc getFloat128Type*(tree: var TypeGraph): TypeId =
-  result = TypeId tree.nodes.len
-  tree.nodes.add TypeNode(x: toX(FloatTy, 128'u32))
-
-proc addBuiltinType*(g: var TypeGraph; id: TypeId) =
-  g.nodes.add g[id]
-
-template firstSon*(n: TypeId): TypeId = TypeId(n.int+1)
-
-proc addType*(g: var TypeGraph; t: TypeId) =
-  # We cannot simply copy `*Decl` nodes. We have to introduce `*Ty` nodes instead:
-  if g[t].kind in {ObjectDecl, UnionDecl}:
-    assert g[t.firstSon].kind == NameVal
-    let name = LitId g[t.firstSon].operand
-    if g[t].kind == ObjectDecl:
-      g.nodes.add TypeNode(x: toX(ObjectTy, name))
-    else:
-      g.nodes.add TypeNode(x: toX(UnionTy, name))
-  else:
-    let pos = t.int
-    let L = span(g, pos)
-    let d = g.nodes.len
-    g.nodes.setLen(d + L)
-    assert L > 0
-    for i in 0..<L:
-      g.nodes[d+i] = g.nodes[pos+i]
-
-proc addArrayLen*(g: var TypeGraph; len: int64) =
-  g.nodes.add TypeNode(x: toX(IntVal, g.lit.numbers.getOrIncl(len)))
-
-proc addSize*(g: var TypeGraph; s: int64) =
-  g.nodes.add TypeNode(x: toX(SizeVal, g.lit.numbers.getOrIncl(s)))
-
-proc addOffset*(g: var TypeGraph; offset: int64) =
-  g.nodes.add TypeNode(x: toX(OffsetVal, g.lit.numbers.getOrIncl(offset)))
-
-proc addAlign*(g: var TypeGraph; a: int64) =
-  g.nodes.add TypeNode(x: toX(AlignVal, g.lit.numbers.getOrIncl(a)))
-
-proc addName*(g: var TypeGraph; name: string) =
-  g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl(name)))
-
-proc addAnnotation*(g: var TypeGraph; name: string) =
-  g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl(name)))
-
-proc addField*(g: var TypeGraph; name: string; typ: TypeId; offset: int64) =
-  let f = g.openType FieldDecl
-  g.addType typ
-  g.addOffset offset
-  g.addName name
-  sealType(g, f)
-
-proc ptrTypeOf*(g: var TypeGraph; t: TypeId): TypeId =
-  let f = g.openType APtrTy
-  g.addType t
-  result = finishType(g, f)
-
-proc arrayPtrTypeOf*(g: var TypeGraph; t: TypeId): TypeId =
-  let f = g.openType AArrayPtrTy
-  g.addType t
-  result = finishType(g, f)
-
-proc store*(r: var RodFile; g: TypeGraph) =
-  storeSeq r, g.nodes
-
-proc load*(r: var RodFile; g: var TypeGraph) =
-  loadSeq r, g.nodes
-
-proc toString*(dest: var string; g: TypeGraph; i: TypeId) =
-  case g[i].kind
-  of VoidTy: dest.add "void"
-  of IntTy:
-    dest.add "i"
-    dest.addInt g[i].operand
-  of UIntTy:
-    dest.add "u"
-    dest.addInt g[i].operand
-  of FloatTy:
-    dest.add "f"
-    dest.addInt g[i].operand
-  of BoolTy:
-    dest.add "b"
-    dest.addInt g[i].operand
-  of CharTy:
-    dest.add "c"
-    dest.addInt g[i].operand
-  of NameVal, AnnotationVal:
-    dest.add g.lit.strings[LitId g[i].operand]
-  of IntVal, SizeVal, AlignVal, OffsetVal:
-    dest.add $g[i].kind
-    dest.add ' '
-    dest.add $g.lit.numbers[LitId g[i].operand]
-  of VarargsTy:
-    dest.add "..."
-  of APtrTy:
-    dest.add "aptr["
-    toString(dest, g, g.elementType(i))
-    dest.add "]"
-  of UPtrTy:
-    dest.add "uptr["
-    toString(dest, g, g.elementType(i))
-    dest.add "]"
-  of AArrayPtrTy:
-    dest.add "aArrayPtr["
-    toString(dest, g, g.elementType(i))
-    dest.add "]"
-  of UArrayPtrTy:
-    dest.add "uArrayPtr["
-    toString(dest, g, g.elementType(i))
-    dest.add "]"
-  of ArrayTy:
-    dest.add "Array["
-    let (elems, len, name) = g.sons3(i)
-    toString(dest, g, elems)
-    dest.add ", "
-    toString(dest, g, len)
-    dest.add ", "
-    toString(dest, g, name)
-    dest.add "]"
-  of LastArrayTy:
-    # array of unspecified size as a last field inside an object
-    dest.add "LastArrayTy["
-    toString(dest, g, g.elementType(i))
-    dest.add "]"
-  of ObjectTy:
-    dest.add "object "
-    dest.add g.lit.strings[LitId g[i].operand]
-  of UnionTy:
-    dest.add "union "
-    dest.add g.lit.strings[LitId g[i].operand]
-  of ProcTy:
-    dest.add "proc["
-    for t in sons(g, i):
-      dest.add ' '
-      toString(dest, g, t)
-    dest.add "]"
-  of ObjectDecl:
-    dest.add "object["
-    for t in sons(g, i):
-      toString(dest, g, t)
-      dest.add '\n'
-    dest.add "]"
-  of UnionDecl:
-    dest.add "union["
-    for t in sons(g, i):
-      toString(dest, g, t)
-      dest.add '\n'
-    dest.add "]"
-  of FieldDecl:
-    dest.add "field["
-    for t in sons(g, i):
-      toString(dest, g, t)
-      dest.add ' '
-    dest.add "]"
-
-    when false:
-      let (typ, offset, name) = g.sons3(i)
-      toString(dest, g, typ)
-      dest.add ' '
-      toString(dest, g, offset)
-      dest.add ' '
-      toString(dest, g, name)
-
-proc toString*(dest: var string; g: TypeGraph) =
-  var i = 0
-  while i < g.len:
-    dest.add "T<"
-    dest.addInt i
-    dest.add "> "
-    toString(dest, g, TypeId i)
-    dest.add '\n'
-    nextChild g, i
-
-iterator allTypes*(g: TypeGraph; start = 0): TypeId =
-  var i = start
-  while i < g.len:
-    yield TypeId i
-    nextChild g, i
-
-iterator allTypesIncludingInner*(g: TypeGraph; start = 0): TypeId =
-  var i = start
-  while i < g.len:
-    yield TypeId i
-    inc i
-
-proc `$`(g: TypeGraph): string =
-  result = ""
-  toString(result, g)
-
-when isMainModule:
-  var g = initTypeGraph(Literals())
-
-  let a = g.openType ArrayTy
-  g.addBuiltinType Int8Id
-  g.addArrayLen 5
-  g.addName "SomeArray"
-  let finalArrayType = finishType(g, a)
-
-  let obj = g.openType ObjectDecl
-  g.nodes.add TypeNode(x: toX(NameVal, g.lit.strings.getOrIncl("MyType")))
-
-  g.addField "p", finalArrayType, 0
-  sealType(g, obj)
-
-  echo g
diff --git a/compiler/nir/nirvm.nim b/compiler/nir/nirvm.nim
deleted file mode 100644
index faa7a1fb7..000000000
--- a/compiler/nir/nirvm.nim
+++ /dev/null
@@ -1,1175 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2023 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-##[ NIR is a little too high level to interpret it efficiently. Thus
-we compute `addresses` for SymIds, labels and offsets for object fields
-in a preprocessing step.
-
-We also split the instruction stream into separate (code, debug) seqs while
-we're at it.
-]##
-
-import std / [syncio, assertions, tables, intsets]
-import ".." / ic / bitabs
-import nirinsts, nirtypes, nirfiles, nirlineinfos
-
-type
-  OpcodeM = enum
-    ImmediateValM,
-    IntValM,
-    StrValM,
-    LoadLocalM, # with local ID
-    LoadGlobalM,
-    LoadProcM,
-    TypedM,   # with type ID
-    PragmaIdM, # with Pragma ID, possible values: see PragmaKey enum
-    NilValM,
-    AllocLocals,
-    SummonParamM,
-    GotoM,
-    CheckedGotoM, # last atom
-
-    ArrayConstrM,
-    ObjConstrM,
-    RetM,
-    YldM,
-
-    SelectM,
-    SelectPairM,  # ((values...), Label)
-    SelectListM,  # (values...)
-    SelectValueM, # (value)
-    SelectRangeM, # (valueA..valueB)
-
-    AddrOfM,
-    ArrayAtM, # (elemSize, addr(a), i)
-    DerefArrayAtM,
-    FieldAtM, # addr(obj.field)
-    DerefFieldAtM,
-
-    LoadM, # a[]
-    AsgnM,  # a = b
-    StoreM, # a[] = b
-    SetExcM,
-    TestExcM,
-
-    CheckedRangeM,
-    CheckedIndexM,
-
-    CallM,
-    CheckedAddM, # with overflow checking etc.
-    CheckedSubM,
-    CheckedMulM,
-    CheckedDivM,
-    CheckedModM,
-    AddM,
-    SubM,
-    MulM,
-    DivM,
-    ModM,
-    BitShlM,
-    BitShrM,
-    BitAndM,
-    BitOrM,
-    BitXorM,
-    BitNotM,
-    BoolNotM,
-    EqM,
-    LeM,
-    LtM,
-    CastM,
-    NumberConvM,
-    CheckedObjConvM,
-    ObjConvM,
-    TestOfM,
-    ProcDeclM,
-    PragmaPairM
-
-const
-  LastAtomicValue = CheckedGotoM
-
-  OpcodeBits = 8'u32
-  OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32
-
-type
-  Instr = distinct uint32
-
-template kind(n: Instr): OpcodeM = OpcodeM(n.uint32 and OpcodeMask)
-template operand(n: Instr): uint32 = (n.uint32 shr OpcodeBits)
-
-template toIns(k: OpcodeM; operand: uint32): Instr =
-  Instr(uint32(k) or (operand shl OpcodeBits))
-
-template toIns(k: OpcodeM; operand: LitId): Instr =
-  Instr(uint32(k) or (operand.uint32 shl OpcodeBits))
-
-type
-  NimStrPayloadVM = object
-    cap: int
-    data: UncheckedArray[char]
-  NimStringVM = object
-    len: int
-    p: ptr NimStrPayloadVM
-
-const
-  GlobalsSize = 1024*24
-
-type
-  PatchPos = distinct int
-  CodePos = distinct int
-
-  Bytecode* = object
-    code: seq[Instr]
-    debug: seq[PackedLineInfo]
-    m: ref NirModule
-    procs: Table[SymId, CodePos]
-    globals: Table[SymId, (uint32, int)]
-    strings: Table[LitId, NimStringVM]
-    globalData: pointer
-    globalsAddr: uint32
-    typeImpls: Table[string, TypeId]
-    offsets: Table[TypeId, seq[(int, TypeId)]]
-    sizes: Table[TypeId, (int, int)] # (size, alignment)
-    oldTypeLen: int
-    procUsagesToPatch: Table[SymId, seq[CodePos]]
-    interactive*: bool
-
-  Universe* = object ## all units: For interpretation we need that
-    units: seq[Bytecode]
-    unitNames: Table[string, int]
-    current: int
-
-proc initBytecode*(m: ref NirModule): Bytecode = Bytecode(m: m, globalData: alloc0(GlobalsSize))
-
-proc debug(bc: Bytecode; t: TypeId) =
-  var buf = ""
-  toString buf, bc.m.types, t
-  echo buf
-
-proc debug(bc: Bytecode; info: PackedLineInfo) =
-  let (litId, line, col) = bc.m.man.unpack(info)
-  echo bc.m.lit.strings[litId], ":", line, ":", col
-
-proc debug(bc: Bytecode; t: Tree; n: NodePos) =
-  var buf = ""
-  toString(t, n, bc.m.lit.strings, bc.m.lit.numbers, bc.m.symnames, buf)
-  echo buf
-
-template `[]`(t: seq[Instr]; n: CodePos): Instr = t[n.int]
-
-proc traverseObject(b: var Bytecode; t, offsetKey: TypeId) =
-  var size = -1
-  var align = -1
-  for x in sons(b.m.types, t):
-    case b.m.types[x].kind
-    of FieldDecl:
-      var offset = -1
-      for y in sons(b.m.types, x):
-        if b.m.types[y].kind == OffsetVal:
-          offset = int(b.m.lit.numbers[b.m.types[y].litId])
-          break
-      b.offsets.mgetOrPut(offsetKey, @[]).add (offset, x.firstSon)
-    of SizeVal:
-      size = int(b.m.lit.numbers[b.m.types[x].litId])
-    of AlignVal:
-      align = int(b.m.lit.numbers[b.m.types[x].litId])
-    of ObjectTy:
-      # inheritance
-      let impl = b.typeImpls.getOrDefault(b.m.lit.strings[b.m.types[x].litId])
-      assert impl.int > 0
-      traverseObject b, impl, offsetKey
-    else: discard
-  if t == offsetKey:
-    b.sizes[t] = (size, align)
-
-proc computeSize(b: var Bytecode; t: TypeId): (int, int) =
-  case b.m.types[t].kind
-  of ObjectDecl, UnionDecl:
-    result = b.sizes[t]
-  of ObjectTy, UnionTy:
-    let impl = b.typeImpls[b.m.lit.strings[b.m.types[t].litId]]
-    result = computeSize(b, impl)
-  of IntTy, UIntTy, FloatTy, BoolTy, CharTy:
-    let s = b.m.types[t].integralBits div 8
-    result = (s, s)
-  of APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, ProcTy:
-    result = (sizeof(pointer), sizeof(pointer))
-  of ArrayTy:
-    let e = elementType(b.m.types, t)
-    let n = arrayLen(b.m.types, t)
-    let inner = computeSize(b, e)
-    result = (inner[0] * n.int, inner[1])
-  else:
-    result = (0, 0)
-
-proc computeElemSize(b: var Bytecode; t: TypeId): int =
-  case b.m.types[t].kind
-  of ArrayTy, APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, LastArrayTy:
-    result = computeSize(b, elementType(b.m.types, t))[0]
-  else:
-    raiseAssert "not an array type"
-
-proc traverseTypes(b: var Bytecode) =
-  for t in allTypes(b.m.types, b.oldTypeLen):
-    if b.m.types[t].kind in {ObjectDecl, UnionDecl}:
-      assert b.m.types[t.firstSon].kind == NameVal
-      b.typeImpls[b.m.lit.strings[b.m.types[t.firstSon].litId]] = t
-
-  for t in allTypes(b.m.types, b.oldTypeLen):
-    if b.m.types[t].kind in {ObjectDecl, UnionDecl}:
-      assert b.m.types[t.firstSon].kind == NameVal
-      traverseObject b, t, t
-  b.oldTypeLen = b.m.types.len
-
-const
-  InvalidPatchPos* = PatchPos(-1)
-
-proc isValid(p: PatchPos): bool {.inline.} = p.int != -1
-
-proc prepare(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM): PatchPos =
-  result = PatchPos bc.code.len
-  bc.code.add toIns(kind, 1'u32)
-  bc.debug.add info
-
-proc add(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; raw: uint32) =
-  bc.code.add toIns(kind, raw)
-  bc.debug.add info
-
-proc add(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; lit: LitId) =
-  add bc, info, kind, uint32(lit)
-
-proc isAtom(bc: Bytecode; pos: int): bool {.inline.} = bc.code[pos].kind <= LastAtomicValue
-proc isAtom(bc: Bytecode; pos: CodePos): bool {.inline.} = bc.code[pos.int].kind <= LastAtomicValue
-
-proc patch(bc: var Bytecode; pos: PatchPos) =
-  let pos = pos.int
-  let k = bc.code[pos].kind
-  assert k > LastAtomicValue
-  let distance = int32(bc.code.len - pos)
-  assert distance > 0
-  bc.code[pos] = toIns(k, cast[uint32](distance))
-
-template build(bc: var Bytecode; info: PackedLineInfo; kind: OpcodeM; body: untyped) =
-  let pos = prepare(bc, info, kind)
-  body
-  patch(bc, pos)
-
-proc len*(bc: Bytecode): int {.inline.} = bc.code.len
-
-template rawSpan(n: Instr): int = int(operand(n))
-
-proc nextChild(bc: Bytecode; pos: var int) {.inline.} =
-  if bc.code[pos].kind > LastAtomicValue:
-    assert bc.code[pos].operand > 0'u32
-    inc pos, bc.code[pos].rawSpan
-  else:
-    inc pos
-
-proc next(bc: Bytecode; pos: var CodePos) {.inline.} = nextChild bc, int(pos)
-
-iterator sons(bc: Bytecode; n: CodePos): CodePos =
-  var pos = n.int
-  assert bc.code[pos].kind > LastAtomicValue
-  let last = pos + bc.code[pos].rawSpan
-  inc pos
-  while pos < last:
-    yield CodePos pos
-    nextChild bc, pos
-
-iterator sonsFrom1(bc: Bytecode; n: CodePos): CodePos =
-  var pos = n.int
-  assert bc.code[pos].kind > LastAtomicValue
-  let last = pos + bc.code[pos].rawSpan
-  inc pos
-  nextChild bc, pos
-  while pos < last:
-    yield CodePos pos
-    nextChild bc, pos
-
-iterator sonsFrom2(bc: Bytecode; n: CodePos): CodePos =
-  var pos = n.int
-  assert bc.code[pos].kind > LastAtomicValue
-  let last = pos + bc.code[pos].rawSpan
-  inc pos
-  nextChild bc, pos
-  nextChild bc, pos
-  while pos < last:
-    yield CodePos pos
-    nextChild bc, pos
-
-template firstSon(n: CodePos): CodePos = CodePos(n.int+1)
-
-template `[]`(t: Bytecode; n: CodePos): Instr = t.code[n.int]
-
-proc span(bc: Bytecode; pos: int): int {.inline.} =
-  if bc.code[pos].kind <= LastAtomicValue: 1 else: int(bc.code[pos].operand)
-
-iterator triples*(bc: Bytecode; n: CodePos): (uint32, int, CodePos) =
-  var pos = n.int
-  assert bc.code[pos].kind > LastAtomicValue
-  let last = pos + bc.code[pos].rawSpan
-  inc pos
-  while pos < last:
-    let offset = bc.code[pos].operand
-    nextChild bc, pos
-    let size = bc.code[pos].operand.int
-    nextChild bc, pos
-    let val = CodePos pos
-    yield (offset, size, val)
-    nextChild bc, pos
-
-proc toString*(t: Bytecode; pos: CodePos;
-               r: var string; nesting = 0) =
-  if r.len > 0 and r[r.len-1] notin {' ', '\n', '(', '[', '{'}:
-    r.add ' '
-
-  case t[pos].kind
-  of ImmediateValM:
-    r.add $t[pos].operand
-  of IntValM:
-    r.add "IntVal "
-    r.add $t.m.lit.numbers[LitId t[pos].operand]
-  of StrValM:
-    escapeToNimLit(t.m.lit.strings[LitId t[pos].operand], r)
-  of LoadLocalM, LoadGlobalM, LoadProcM, AllocLocals, SummonParamM:
-    r.add $t[pos].kind
-    r.add ' '
-    r.add $t[pos].operand
-  of PragmaIdM:
-    r.add $cast[PragmaKey](t[pos].operand)
-  of TypedM:
-    r.add "T<"
-    r.add $t[pos].operand
-    r.add ">"
-  of NilValM:
-    r.add "NilVal"
-  of GotoM, CheckedGotoM:
-    r.add $t[pos].kind
-    r.add " L"
-    r.add $t[pos].operand
-  else:
-    r.add $t[pos].kind
-    r.add "{\n"
-    for i in 0..<(nesting+1)*2: r.add ' '
-    for p in sons(t, pos):
-      toString t, p, r, nesting+1
-    r.add "\n"
-    for i in 0..<nesting*2: r.add ' '
-    r.add "}"
-
-proc debug(b: Bytecode; pos: CodePos) =
-  var buf = ""
-  toString(b, pos, buf)
-  echo buf
-
-type
-  Preprocessing = object
-    u: ref Universe
-    known: Table[LabelId, CodePos]
-    toPatch: Table[LabelId, seq[CodePos]]
-    locals: Table[SymId, (uint32, int)] # address, size
-    thisModule: uint32
-    localsAddr: uint32
-    markedWithLabel: IntSet
-
-proc align(address, alignment: uint32): uint32 =
-  result = (address + (alignment - 1'u32)) and not (alignment - 1'u32)
-
-proc genGoto(c: var Preprocessing; bc: var Bytecode; info: PackedLineInfo; lab: LabelId; opc: OpcodeM) =
-  let dest = c.known.getOrDefault(lab, CodePos(-1))
-  if dest.int >= 0:
-    bc.add info, opc, uint32 dest
-  else:
-    let here = CodePos(bc.code.len)
-    c.toPatch.mgetOrPut(lab, @[]).add here
-    bc.add info, opc, 1u32 # will be patched once we traversed the label
-
-type
-  AddrMode = enum
-    InDotExpr, WantAddr
-
-template maybeDeref(doDeref: bool; size: int; body: untyped) =
-  var pos = PatchPos(-1)
-  if doDeref:
-    pos = prepare(bc, info, LoadM)
-    bc.add info, ImmediateValM, uint32 size
-  body
-  if doDeref:
-    patch(bc, pos)
-
-proc toReadonlyString(s: string): NimStringVM =
-  if s.len == 0:
-    result = NimStringVM(len: 0, p: nil)
-  else:
-    result = NimStringVM(len: s.len, p: cast[ptr NimStrPayloadVM](alloc(s.len+1+sizeof(int))))
-    copyMem(addr result.p.data[0], addr s[0], s.len+1)
-    result.p.cap = s.len or (1 shl (8 * 8 - 2)) # see also NIM_STRLIT_FLAG
-
-const
-  ForwardedProc = 10_000_000'u32
-
-proc preprocess(c: var Preprocessing; bc: var Bytecode; t: Tree; n: NodePos; flags: set[AddrMode]) =
-  let info = t[n].info
-
-  template recurse(opc) =
-    build bc, info, opc:
-      for ch in sons(t, n): preprocess(c, bc, t, ch, {WantAddr})
-
-  case t[n].kind
-  of Nop, ForeignDecl, ForeignProcDecl:
-    discard "don't use Nop"
-  of ImmediateVal:
-    bc.add info, ImmediateValM, t[n].rawOperand
-  of IntVal:
-    bc.add info, IntValM, t[n].rawOperand
-  of StrVal:
-    let litId = LitId t[n].rawOperand
-    if not bc.strings.hasKey(litId):
-      bc.strings[litId] = toReadonlyString(bc.m.lit.strings[litId])
-    bc.add info, StrValM, t[n].rawOperand
-  of SymDef:
-    discard "happens for proc decls. Don't copy the node as we don't need it"
-  of SymUse:
-    let s = t[n].symId
-    if c.locals.hasKey(s):
-      let (address, size) = c.locals[s]
-      maybeDeref(WantAddr notin flags, size):
-        bc.add info, LoadLocalM, address
-    elif bc.procs.hasKey(s):
-      bc.add info, LoadProcM, uint32 bc.procs[s]
-    elif bc.globals.hasKey(s):
-      let (address, size) = bc.globals[s]
-      maybeDeref(WantAddr notin flags, size):
-        bc.add info, LoadGlobalM, address
-    else:
-      let here = CodePos(bc.code.len)
-      bc.add info, LoadProcM, ForwardedProc + uint32(s)
-      bc.procUsagesToPatch.mgetOrPut(s, @[]).add here
-      #raiseAssert "don't understand SymUse ID " & $int(s)
-
-  of ModuleSymUse:
-    when false:
-      let (x, y) = sons2(t, n)
-      let unit = c.u.unitNames.getOrDefault(bc.m.lit.strings[t[x].litId], -1)
-      let s = t[y].symId
-      if c.u.units[unit].procs.hasKey(s):
-        bc.add info, LoadProcM, uint32 c.u.units[unit].procs[s]
-      elif bc.globals.hasKey(s):
-        maybeDeref(WantAddr notin flags):
-          build bc, info, LoadGlobalM:
-            bc.add info, ImmediateValM, uint32 unit
-            bc.add info, LoadLocalM, uint32 s
-      else:
-        raiseAssert "don't understand ModuleSymUse ID"
-
-    raiseAssert "don't understand ModuleSymUse ID"
-  of Typed:
-    bc.add info, TypedM, t[n].rawOperand
-  of PragmaId:
-    bc.add info, PragmaIdM, t[n].rawOperand
-  of NilVal:
-    bc.add info, NilValM, t[n].rawOperand
-  of LoopLabel, Label:
-    let lab = t[n].label
-    let here = CodePos(bc.code.len)
-    c.known[lab] = here
-    var p: seq[CodePos] = @[]
-    if c.toPatch.take(lab, p):
-      for x in p: (bc.code[x]) = toIns(bc.code[x].kind, uint32 here)
-    c.markedWithLabel.incl here.int # for toString()
-  of Goto, GotoLoop:
-    c.genGoto(bc, info, t[n].label, GotoM)
-  of CheckedGoto:
-    c.genGoto(bc, info, t[n].label, CheckedGotoM)
-  of ArrayConstr:
-    let typ = t[n.firstSon].typeId
-    let s = computeElemSize(bc, typ)
-    build bc, info, ArrayConstrM:
-      bc.add info, ImmediateValM, uint32 s
-      for ch in sonsFrom1(t, n):
-        preprocess(c, bc, t, ch, {WantAddr})
-  of ObjConstr:
-    #debug bc, t, n
-    var i = 0
-    let typ = t[n.firstSon].typeId
-    build bc, info, ObjConstrM:
-      for ch in sons(t, n):
-        if i > 0:
-          if (i mod 2) == 1:
-            let (offset, typ) = bc.offsets[typ][t[ch].immediateVal]
-            let size = computeSize(bc, typ)[0]
-            bc.add info, ImmediateValM, uint32(offset)
-            bc.add info, ImmediateValM, uint32(size)
-          else:
-            preprocess(c, bc, t, ch, {WantAddr})
-        inc i
-  of Ret:
-    recurse RetM
-  of Yld:
-    recurse YldM
-  of Select:
-    recurse SelectM
-  of SelectPair:
-    recurse SelectPairM
-  of SelectList:
-    recurse SelectListM
-  of SelectValue:
-    recurse SelectValueM
-  of SelectRange:
-    recurse SelectRangeM
-  of SummonGlobal, SummonThreadLocal, SummonConst:
-    let (typ, sym) = sons2(t, n)
-
-    let s = t[sym].symId
-    let tid = t[typ].typeId
-    let (size, alignment) = computeSize(bc, tid)
-
-    let global = align(bc.globalsAddr, uint32 alignment)
-    bc.globals[s] = (global, size)
-    bc.globalsAddr += uint32 size
-    assert bc.globalsAddr < GlobalsSize
-
-  of Summon:
-    let (typ, sym) = sons2(t, n)
-
-    let s = t[sym].symId
-    let tid = t[typ].typeId
-    let (size, alignment) = computeSize(bc, tid)
-
-    let local = align(c.localsAddr, uint32 alignment)
-    c.locals[s] = (local, size)
-    c.localsAddr += uint32 size
-    # allocation is combined into the frame allocation so there is no
-    # instruction to emit
-  of SummonParam, SummonResult:
-    let (typ, sym) = sons2(t, n)
-
-    let s = t[sym].symId
-    let tid = t[typ].typeId
-    let (size, alignment) = computeSize(bc, tid)
-
-    let local = align(c.localsAddr, uint32 alignment)
-    c.locals[s] = (local, size)
-    c.localsAddr += uint32 size
-    bc.add info, SummonParamM, local
-    bc.add info, ImmediateValM, uint32 size
-  of Kill:
-    discard "we don't care about Kill instructions"
-  of AddrOf:
-    let (_, arg) = sons2(t, n)
-    preprocess(c, bc, t, arg, {WantAddr})
-    # the address of x is what the VM works with all the time so there is
-    # nothing to compute.
-  of ArrayAt:
-    let (arrayType, a, i) = sons3(t, n)
-    let tid = t[arrayType].typeId
-    let size = uint32 computeElemSize(bc, tid)
-    build bc, info, ArrayAtM:
-      bc.add info, ImmediateValM, size
-      preprocess(c, bc, t, a, {WantAddr})
-      preprocess(c, bc, t, i, {WantAddr})
-  of DerefArrayAt:
-    let (arrayType, a, i) = sons3(t, n)
-    let tid = t[arrayType].typeId
-    let size = uint32 computeElemSize(bc, tid)
-    build bc, info, DerefArrayAtM:
-      bc.add info, ImmediateValM, size
-      preprocess(c, bc, t, a, {WantAddr})
-      preprocess(c, bc, t, i, {WantAddr})
-  of FieldAt:
-    let (typ, a, b) = sons3(t, n)
-    let offset = bc.offsets[t[typ].typeId][t[b].immediateVal][0]
-    build bc, info, FieldAtM:
-      preprocess(c, bc, t, a, flags+{WantAddr})
-      bc.add info, ImmediateValM, uint32(offset)
-  of DerefFieldAt:
-    let (typ, a, b) = sons3(t, n)
-    let offset = bc.offsets[t[typ].typeId][t[b].immediateVal][0]
-    build bc, info, DerefFieldAtM:
-      preprocess(c, bc, t, a, flags+{WantAddr})
-      bc.add info, ImmediateValM, uint32(offset)
-  of Load:
-    let (elemType, a) = sons2(t, n)
-    let tid = t[elemType].typeId
-    build bc, info, LoadM:
-      bc.add info, ImmediateValM, uint32 computeSize(bc, tid)[0]
-      preprocess(c, bc, t, a, {})
-
-  of Store:
-    raiseAssert "Assumption was that Store is unused!"
-  of Asgn:
-    let (elemType, dest, src) = sons3(t, n)
-    let tid = t[elemType].typeId
-    if t[src].kind in {Call, IndirectCall}:
-      # No support for return values, these are mapped to `var T` parameters!
-      build bc, info, CallM:
-        preprocess(c, bc, t, src.skipTyped, {WantAddr})
-        preprocess(c, bc, t, dest, {WantAddr})
-        for ch in sonsFromN(t, src, 2): preprocess(c, bc, t, ch, {WantAddr})
-    elif t[src].kind in {CheckedCall, CheckedIndirectCall}:
-      let (_, gotoInstr, fn) = sons3(t, src)
-      build bc, info, CallM:
-        preprocess(c, bc, t, fn, {WantAddr})
-        preprocess(c, bc, t, dest, {WantAddr})
-        for ch in sonsFromN(t, src, 3): preprocess(c, bc, t, ch, {WantAddr})
-      preprocess c, bc, t, gotoInstr, {}
-    elif t[dest].kind == Load:
-      let (typ, a) = sons2(t, dest)
-      let s = computeSize(bc, tid)[0]
-      build bc, info, StoreM:
-        bc.add info, ImmediateValM, uint32 s
-        preprocess(c, bc, t, a, {WantAddr})
-        preprocess(c, bc, t, src, {})
-    else:
-      let s = computeSize(bc, tid)[0]
-      build bc, info, AsgnM:
-        bc.add info, ImmediateValM, uint32 s
-        preprocess(c, bc, t, dest, {WantAddr})
-        preprocess(c, bc, t, src, {})
-  of SetExc:
-    recurse SetExcM
-  of TestExc:
-    recurse TestExcM
-  of CheckedRange:
-    recurse CheckedRangeM
-  of CheckedIndex:
-    recurse CheckedIndexM
-  of Call, IndirectCall:
-    # avoid the Typed thing at position 0:
-    build bc, info, CallM:
-      for ch in sonsFrom1(t, n): preprocess(c, bc, t, ch, {WantAddr})
-  of CheckedCall, CheckedIndirectCall:
-    # avoid the Typed thing at position 0:
-    let (_, gotoInstr, fn) = sons3(t, n)
-    build bc, info, CallM:
-      preprocess(c, bc, t, fn, {WantAddr})
-      for ch in sonsFromN(t, n, 3): preprocess(c, bc, t, ch, {WantAddr})
-    preprocess c, bc, t, gotoInstr, {WantAddr}
-  of CheckedAdd:
-    recurse CheckedAddM
-  of CheckedSub:
-    recurse CheckedSubM
-  of CheckedMul:
-    recurse CheckedMulM
-  of CheckedDiv:
-    recurse CheckedDivM
-  of CheckedMod:
-    recurse CheckedModM
-  of Add:
-    recurse AddM
-  of Sub:
-    recurse SubM
-  of Mul:
-    recurse MulM
-  of Div:
-    recurse DivM
-  of Mod:
-    recurse ModM
-  of BitShl:
-    recurse BitShlM
-  of BitShr:
-    recurse BitShrM
-  of BitAnd:
-    recurse BitAndM
-  of BitOr:
-    recurse BitOrM
-  of BitXor:
-    recurse BitXorM
-  of BitNot:
-    recurse BitNotM
-  of BoolNot:
-    recurse BoolNotM
-  of Eq:
-    recurse EqM
-  of Le:
-    recurse LeM
-  of Lt:
-    recurse LtM
-  of Cast:
-    recurse CastM
-  of NumberConv:
-    recurse NumberConvM
-  of CheckedObjConv:
-    recurse CheckedObjConvM
-  of ObjConv:
-    recurse ObjConvM
-  of TestOf:
-    recurse TestOfM
-  of Emit:
-    raiseAssert "cannot interpret: Emit"
-  of ProcDecl:
-    var c2 = Preprocessing(u: c.u, thisModule: c.thisModule)
-    let sym = t[n.firstSon].symId
-    let here = CodePos(bc.len)
-    var p: seq[CodePos] = @[]
-    if bc.procUsagesToPatch.take(sym, p):
-      for x in p: (bc.code[x]) = toIns(bc.code[x].kind, uint32 here)
-    bc.procs[sym] = here
-    build bc, info, ProcDeclM:
-      let toPatch = bc.code.len
-      bc.add info, AllocLocals, 0'u32
-      for ch in sons(t, n): preprocess(c2, bc, t, ch, {})
-      bc.code[toPatch] = toIns(AllocLocals, c2.localsAddr)
-    when false:
-      if here.int == 39850:
-        debug bc, t, n
-        debug bc, here
-
-  of PragmaPair:
-    recurse PragmaPairM
-
-const PayloadSize = 128
-
-type
-  StackFrame = ref object
-    locals: pointer   # usually points into `payload` if size is small enough, otherwise it's `alloc`'ed.
-    payload: array[PayloadSize, byte]
-    caller: StackFrame
-    returnAddr: CodePos
-    jumpTo: CodePos # exception handling
-    u: ref Universe
-
-proc newStackFrame(size: int; caller: StackFrame; returnAddr: CodePos): StackFrame =
-  result = StackFrame(caller: caller, returnAddr: returnAddr, u: caller.u)
-  if size <= PayloadSize:
-    result.locals = addr(result.payload)
-  else:
-    result.locals = alloc0(size)
-
-proc popStackFrame(s: StackFrame): StackFrame =
-  if s.locals != addr(s.payload):
-    dealloc s.locals
-  result = s.caller
-
-template `+!`(p: pointer; diff: uint): pointer = cast[pointer](cast[uint](p) + diff)
-
-proc isAtom(tree: seq[Instr]; pos: CodePos): bool {.inline.} = tree[pos.int].kind <= LastAtomicValue
-
-proc span(bc: seq[Instr]; pos: int): int {.inline.} =
-  if bc[pos].kind <= LastAtomicValue: 1 else: int(bc[pos].operand)
-
-proc sons2(tree: seq[Instr]; n: CodePos): (CodePos, CodePos) =
-  assert(not isAtom(tree, n))
-  let a = n.int+1
-  let b = a + span(tree, a)
-  result = (CodePos a, CodePos b)
-
-proc sons3(tree: seq[Instr]; n: CodePos): (CodePos, CodePos, CodePos) =
-  assert(not isAtom(tree, n))
-  let a = n.int+1
-  let b = a + span(tree, a)
-  let c = b + span(tree, b)
-  result = (CodePos a, CodePos b, CodePos c)
-
-proc sons4(tree: seq[Instr]; n: CodePos): (CodePos, CodePos, CodePos, CodePos) =
-  assert(not isAtom(tree, n))
-  let a = n.int+1
-  let b = a + span(tree, a)
-  let c = b + span(tree, b)
-  let d = c + span(tree, c)
-  result = (CodePos a, CodePos b, CodePos c, CodePos d)
-
-proc typeId*(ins: Instr): TypeId {.inline.} =
-  assert ins.kind == TypedM
-  result = TypeId(ins.operand)
-
-proc immediateVal*(ins: Instr): int {.inline.} =
-  assert ins.kind == ImmediateValM
-  result = cast[int](ins.operand)
-
-proc litId*(ins: Instr): LitId {.inline.} =
-  assert ins.kind in {StrValM, IntValM}
-  result = LitId(ins.operand)
-
-proc eval(c: Bytecode; pc: CodePos; s: StackFrame; result: pointer; size: int)
-
-proc evalAddr(c: Bytecode; pc: CodePos; s: StackFrame): pointer =
-  case c.code[pc].kind
-  of LoadLocalM:
-    result = s.locals +! c.code[pc].operand
-  of FieldAtM:
-    let (x, offset) = sons2(c.code, pc)
-    result = evalAddr(c, x, s)
-    result = result +! c.code[offset].operand
-  of DerefFieldAtM:
-    let (x, offset) = sons2(c.code, pc)
-    let p = evalAddr(c, x, s)
-    result = cast[ptr pointer](p)[] +! c.code[offset].operand
-  of ArrayAtM:
-    let (e, a, i) = sons3(c.code, pc)
-    let elemSize = c.code[e].operand
-    result = evalAddr(c, a, s)
-    var idx: int = 0
-    eval(c, i, s, addr idx, sizeof(int))
-    result = result +! (uint32(idx) * elemSize)
-  of DerefArrayAtM:
-    let (e, a, i) = sons3(c.code, pc)
-    let elemSize = c.code[e].operand
-    var p = evalAddr(c, a, s)
-    var idx: int = 0
-    eval(c, i, s, addr idx, sizeof(int))
-    result = cast[ptr pointer](p)[] +! (uint32(idx) * elemSize)
-  of LoadGlobalM:
-    result = c.globalData +! c.code[pc].operand
-  else:
-    raiseAssert("unimplemented addressing mode")
-
-proc `div`(x, y: float32): float32 {.inline.} = x / y
-proc `div`(x, y: float64): float64 {.inline.} = x / y
-
-from std / math import `mod`
-
-template binop(opr) {.dirty.} =
-  template impl(typ) {.dirty.} =
-    var x = default(typ)
-    var y = default(typ)
-    eval c, a, s, addr x, sizeof(typ)
-    eval c, b, s, addr y, sizeof(typ)
-    cast[ptr typ](result)[] = opr(x, y)
-
-  let (t, a, b) = sons3(c.code, pc)
-  let tid = TypeId c.code[t].operand
-  case tid
-  of Bool8Id, Char8Id, UInt8Id: impl uint8
-  of Int8Id: impl int8
-  of Int16Id: impl int16
-  of Int32Id: impl int32
-  of Int64Id: impl int64
-  of UInt16Id: impl uint16
-  of UInt32Id: impl uint32
-  of UInt64Id: impl uint64
-  of Float32Id: impl float32
-  of Float64Id: impl float64
-  else: discard
-
-template checkedBinop(opr) {.dirty.} =
-  template impl(typ) {.dirty.} =
-    var x = default(typ)
-    var y = default(typ)
-    eval c, a, s, addr x, sizeof(typ)
-    eval c, b, s, addr y, sizeof(typ)
-    try:
-      cast[ptr typ](result)[] = opr(x, y)
-    except OverflowDefect, DivByZeroDefect:
-      s.jumpTo = CodePos c.code[j].operand
-
-  let (t, j, a, b) = sons4(c.code, pc)
-  let tid = TypeId c.code[t].operand
-  case tid
-  of Bool8Id, Char8Id, UInt8Id: impl uint8
-  of Int8Id: impl int8
-  of Int16Id: impl int16
-  of Int32Id: impl int32
-  of Int64Id: impl int64
-  of UInt16Id: impl uint16
-  of UInt32Id: impl uint32
-  of UInt64Id: impl uint64
-  of Float32Id: impl float32
-  of Float64Id: impl float64
-  else: discard
-
-template bitop(opr) {.dirty.} =
-  template impl(typ) {.dirty.} =
-    var x = default(typ)
-    var y = default(typ)
-    eval c, a, s, addr x, sizeof(typ)
-    eval c, b, s, addr y, sizeof(typ)
-    cast[ptr typ](result)[] = opr(x, y)
-
-  let (t, a, b) = sons3(c.code, pc)
-  let tid = c.code[t].typeId
-  case tid
-  of Bool8Id, Char8Id, UInt8Id: impl uint8
-  of Int8Id: impl int8
-  of Int16Id: impl int16
-  of Int32Id: impl int32
-  of Int64Id: impl int64
-  of UInt16Id: impl uint16
-  of UInt32Id: impl uint32
-  of UInt64Id: impl uint64
-  else: discard
-
-template cmpop(opr) {.dirty.} =
-  template impl(typ) {.dirty.} =
-    var x = default(typ)
-    var y = default(typ)
-    eval c, a, s, addr x, sizeof(typ)
-    eval c, b, s, addr y, sizeof(typ)
-    cast[ptr bool](result)[] = opr(x, y)
-
-  let (t, a, b) = sons3(c.code, pc)
-  let tid = c.code[t].typeId
-  case tid
-  of Bool8Id, Char8Id, UInt8Id: impl uint8
-  of Int8Id: impl int8
-  of Int16Id: impl int16
-  of Int32Id: impl int32
-  of Int64Id: impl int64
-  of UInt16Id: impl uint16
-  of UInt32Id: impl uint32
-  of UInt64Id: impl uint64
-  of Float32Id: impl float32
-  of Float64Id: impl float64
-  else: discard
-
-proc evalSelect(c: Bytecode; pc: CodePos; s: StackFrame): CodePos =
-  template impl(typ) {.dirty.} =
-    var selector = default(typ)
-    eval c, sel, s, addr selector, sizeof(typ)
-    for pair in sonsFrom2(c, pc):
-      assert c.code[pair].kind == SelectPairM
-      let (values, action) = sons2(c.code, pair)
-      if c.code[values].kind == SelectValueM:
-        var a = default(typ)
-        eval c, values.firstSon, s, addr a, sizeof(typ)
-        if selector == a:
-          return CodePos c.code[action].operand
-      else:
-        assert c.code[values].kind == SelectListM, $c.code[values].kind
-        for v in sons(c, values):
-          case c.code[v].kind
-          of SelectValueM:
-            var a = default(typ)
-            eval c, v.firstSon, s, addr a, sizeof(typ)
-            if selector == a:
-              return CodePos c.code[action].operand
-          of SelectRangeM:
-            let (va, vb) = sons2(c.code, v)
-            var a = default(typ)
-            eval c, va, s, addr a, sizeof(typ)
-            var b = default(typ)
-            eval c, vb, s, addr a, sizeof(typ)
-            if a <= selector and selector <= b:
-              return CodePos c.code[action].operand
-          else: raiseAssert "unreachable"
-    result = CodePos(-1)
-
-  let (t, sel) = sons2(c.code, pc)
-  let tid = c.code[t].typeId
-  case tid
-  of Bool8Id, Char8Id, UInt8Id: impl uint8
-  of Int8Id: impl int8
-  of Int16Id: impl int16
-  of Int32Id: impl int32
-  of Int64Id: impl int64
-  of UInt16Id: impl uint16
-  of UInt32Id: impl uint32
-  of UInt64Id: impl uint64
-  else: raiseAssert "unreachable"
-
-proc eval(c: Bytecode; pc: CodePos; s: StackFrame; result: pointer; size: int) =
-  case c.code[pc].kind
-  of LoadLocalM:
-    let src = s.locals +! c.code[pc].operand
-    copyMem result, src, size
-  of FieldAtM, DerefFieldAtM, ArrayAtM, DerefArrayAtM, LoadGlobalM:
-    let src = evalAddr(c, pc, s)
-    copyMem result, src, size
-  of LoadProcM:
-    let procAddr = c.code[pc].operand
-    cast[ptr pointer](result)[] = cast[pointer](procAddr)
-  of LoadM:
-    let (_, arg) = sons2(c.code, pc)
-    let src = evalAddr(c, arg, s)
-    copyMem result, src, size
-  of CheckedAddM: checkedBinop `+`
-  of CheckedSubM: checkedBinop `-`
-  of CheckedMulM: checkedBinop `*`
-  of CheckedDivM: checkedBinop `div`
-  of CheckedModM: checkedBinop `mod`
-  of AddM: binop `+`
-  of SubM: binop `-`
-  of MulM: binop `*`
-  of DivM: binop `div`
-  of ModM: binop `mod`
-  of BitShlM: bitop `shl`
-  of BitShrM: bitop `shr`
-  of BitAndM: bitop `and`
-  of BitOrM: bitop `or`
-  of BitXorM: bitop `xor`
-  of EqM: cmpop `==`
-  of LeM: cmpop `<=`
-  of LtM: cmpop `<`
-
-  of StrValM:
-    # binary compatible and no deep copy required:
-    copyMem(cast[ptr string](result), addr(c.strings[c[pc].litId]), sizeof(string))
-  of ObjConstrM:
-    for offset, size, val in triples(c, pc):
-      eval c, val, s, result+!offset, size
-  of ArrayConstrM:
-    let elemSize = c.code[pc.firstSon].operand
-    var r = result
-    for ch in sonsFrom1(c, pc):
-      eval c, ch, s, r, elemSize.int
-      r = r+!elemSize # can even do strength reduction here!
-  of NumberConvM:
-    let (t, x) = sons2(c.code, pc)
-    let word = if c[x].kind == NilValM: 0'i64 else: c.m.lit.numbers[c[x].litId]
-
-    template impl(typ: typedesc) {.dirty.} =
-      cast[ptr typ](result)[] = cast[typ](word)
-
-    let tid = c.code[t].typeId
-    case tid
-    of Bool8Id, Char8Id, UInt8Id: impl uint8
-    of Int8Id: impl int8
-    of Int16Id: impl int16
-    of Int32Id: impl int32
-    of Int64Id: impl int64
-    of UInt16Id: impl uint16
-    of UInt32Id: impl uint32
-    of UInt64Id: impl uint64
-    of Float32Id: impl float32
-    of Float64Id: impl float64
-    else:
-      case c.m.types[tid].kind
-      of ProcTy, UPtrTy, APtrTy, AArrayPtrTy, UArrayPtrTy:
-        # the VM always uses 64 bit pointers:
-        impl uint64
-      else:
-        raiseAssert "cannot happen: " & $c.m.types[tid].kind
-  else:
-    #debug c, c.debug[pc.int]
-    raiseAssert "cannot happen: " & $c.code[pc].kind
-
-proc evalProc(c: Bytecode; pc: CodePos; s: StackFrame): CodePos =
-  assert c.code[pc].kind == LoadProcM
-  let procSym = c[pc].operand
-  when false:
-    if procSym >= ForwardedProc:
-      for k, v in c.procUsagesToPatch:
-        if uint32(k) == procSym - ForwardedProc:
-          echo k.int, " ", v.len, " <-- this one"
-        else:
-          echo k.int, " ", v.len
-
-  assert procSym < ForwardedProc
-  result = CodePos(procSym)
-
-proc echoImpl(c: Bytecode; pc: CodePos; frame: StackFrame) =
-  var s = default(NimStringVM)
-  for a in sonsFrom1(c, pc):
-    assert c[a].kind == ArrayConstrM
-    let elemSize = c.code[a.firstSon].operand.int
-    for ch in sonsFrom1(c, a):
-      eval c, ch, frame, addr s, elemSize
-      if s.len > 0:
-        discard stdout.writeBuffer(addr(s.p.data[0]), s.len)
-  stdout.write "\n"
-  stdout.flushFile()
-
-type
-  EvalBuiltinState = enum
-    DidNothing, DidEval, DidError
-
-proc evalBuiltin(c: Bytecode; pc: CodePos; s: StackFrame; prc: CodePos; state: var EvalBuiltinState): CodePos =
-  var prc = prc
-  while true:
-    case c[prc].kind
-    of PragmaPairM:
-      let (x, y) = sons2(c.code, prc)
-      let key = cast[PragmaKey](c[x].operand)
-      case key
-      of CoreName:
-        let lit = c[y].litId
-        case c.m.lit.strings[lit]
-        of "echoBinSafe": echoImpl(c, pc, s)
-        else:
-          raiseAssert "cannot eval: " & c.m.lit.strings[lit]
-        state = DidEval
-      of HeaderImport, DllImport:
-        let lit = c[y].litId
-        raiseAssert "cannot eval: " & c.m.lit.strings[lit]
-      else: discard
-    of PragmaIdM, AllocLocals: discard
-    else: break
-    next c, prc
-  result = prc
-
-proc exec(c: Bytecode; pc: CodePos; u: ref Universe) =
-  var pc = pc
-  var frame = StackFrame(u: u)
-  while pc.int < c.code.len:
-    when false: # c.interactive:
-      echo "running: ", pc.int
-      debug c, pc
-
-    case c.code[pc].kind
-    of GotoM:
-      pc = CodePos(c.code[pc].operand)
-    of AsgnM:
-      let (sz, a, b) = sons3(c.code, pc)
-      let dest = evalAddr(c, a, frame)
-      eval(c, b, frame, dest, c.code[sz].operand.int)
-      next c, pc
-    of StoreM:
-      let (sz, a, b) = sons3(c.code, pc)
-      let destPtr = evalAddr(c, a, frame)
-      let dest = cast[ptr pointer](destPtr)[]
-      eval(c, b, frame, dest, c.code[sz].operand.int)
-      next c, pc
-    of CallM:
-      # No support for return values, these are mapped to `var T` parameters!
-      var prc = evalProc(c, pc.firstSon, frame)
-      assert c.code[prc.firstSon].kind == AllocLocals
-      let frameSize = int c.code[prc.firstSon].operand
-      # skip stupid stuff:
-      var evalState = DidNothing
-      prc = evalBuiltin(c, pc, frame, prc.firstSon, evalState)
-      if evalState != DidNothing:
-        next c, pc
-        if pc.int < c.code.len and c.code[pc].kind == CheckedGotoM:
-          if evalState == DidEval:
-            next c, pc
-          else:
-            pc = CodePos(c.code[pc].operand)
-      else:
-        # setup storage for the proc already:
-        let callInstr = pc
-        next c, pc
-        let s2 = newStackFrame(frameSize, frame, pc)
-        for a in sonsFrom1(c, callInstr):
-          assert c[prc].kind == SummonParamM
-          let paramAddr = c[prc].operand
-          next c, prc
-          assert c[prc].kind == ImmediateValM
-          let paramSize = c[prc].operand.int
-          next c, prc
-          eval(c, a, s2, s2.locals +! paramAddr, paramSize)
-        frame = s2
-        pc = prc
-    of RetM:
-      pc = frame.returnAddr
-      if c.code[pc].kind == CheckedGotoM:
-        pc = frame.jumpTo
-      frame = popStackFrame(frame)
-    of SelectM:
-      let pc2 = evalSelect(c, pc, frame)
-      if pc2.int >= 0:
-        pc = pc2
-      else:
-        next c, pc
-    of ProcDeclM:
-      next c, pc
-    else:
-      #debug c, c.debug[pc.int]
-      raiseAssert "unreachable: " & $c.code[pc].kind
-
-proc execCode*(bc: var Bytecode; t: Tree; n: NodePos) =
-  traverseTypes bc
-  var c = Preprocessing(u: nil, thisModule: 1'u32)
-  let start = CodePos(bc.code.len)
-  var pc = n
-  while pc.int < t.len:
-    #if bc.interactive:
-    #  echo "RUnning: "
-    #  debug bc, t, pc
-    preprocess c, bc, t, pc, {}
-    next t, pc
-  exec bc, start, nil
diff --git a/compiler/nir/stringcases.nim b/compiler/nir/stringcases.nim
deleted file mode 100644
index afdf8fda4..000000000
--- a/compiler/nir/stringcases.nim
+++ /dev/null
@@ -1,200 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2023 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## included from ast2ir.nim
-
-#[
-
-case s
-of "abc", "abbd":
-  echo 1
-of "hah":
-  echo 2
-of "gah":
-  echo 3
-
-# we produce code like this:
-
-if s[0] <= 'a':
-  if s == "abc: goto L1
-  elif s == "abbd": goto L1
-else:
-  if s[2] <= 'h':
-    if s == "hah": goto L2
-    elif s == "gah": goto L3
-goto afterCase
-
-L1:
-  echo 1
-  goto afterCase
-L2:
-  echo 2
-  goto afterCase
-L3:
-  echo 3
-  goto afterCase
-
-afterCase: ...
-
-]#
-
-# We split the set of strings into 2 sets of roughly the same size.
-# The condition used for splitting is a (position, char) tuple.
-# Every string of length > position for which s[position] <= char is in one
-# set else it is in the other set.
-
-from std/sequtils import addUnique
-
-type
-  Key = (LitId, LabelId)
-
-proc splitValue(strings: BiTable[string]; a: openArray[Key]; position: int): (char, float) =
-  var cand: seq[char] = @[]
-  for t in items a:
-    let s = strings[t[0]]
-    if s.len > position: cand.addUnique s[position]
-
-  result = ('\0', -1.0)
-  for disc in items cand:
-    var hits = 0
-    for t in items a:
-      let s = strings[t[0]]
-      if s.len > position and s[position] <= disc:
-        inc hits
-    # the split is the better, the more `hits` is close to `a.len / 2`:
-    let grade = 100000.0 - abs(hits.float - a.len.float / 2.0)
-    if grade > result[1]:
-      result = (disc, grade)
-
-proc tryAllPositions(strings: BiTable[string]; a: openArray[Key]): (char, int) =
-  var m = 0
-  for t in items a:
-    m = max(m, strings[t[0]].len)
-
-  result = ('\0', -1)
-  var best = -1.0
-  for i in 0 ..< m:
-    let current = splitValue(strings, a, i)
-    if current[1] > best:
-      best = current[1]
-      result = (current[0], i)
-
-type
-  SearchKind = enum
-    LinearSearch, SplitSearch
-  SearchResult* = object
-    case kind: SearchKind
-    of LinearSearch:
-      a: seq[Key]
-    of SplitSearch:
-      span: int
-      best: (char, int)
-
-proc emitLinearSearch(strings: BiTable[string]; a: openArray[Key]; dest: var seq[SearchResult]) =
-  var d = SearchResult(kind: LinearSearch, a: @[])
-  for x in a: d.a.add x
-  dest.add d
-
-proc split(strings: BiTable[string]; a: openArray[Key]; dest: var seq[SearchResult]) =
-  if a.len <= 4:
-    emitLinearSearch strings, a, dest
-  else:
-    let best = tryAllPositions(strings, a)
-    var groupA: seq[Key] = @[]
-    var groupB: seq[Key] = @[]
-    for t in items a:
-      let s = strings[t[0]]
-      if s.len > best[1] and s[best[1]] <= best[0]:
-        groupA.add t
-      else:
-        groupB.add t
-    if groupA.len == 0 or groupB.len == 0:
-      emitLinearSearch strings, a, dest
-    else:
-      let toPatch = dest.len
-      dest.add SearchResult(kind: SplitSearch, span: 1, best: best)
-      split strings, groupA, dest
-      split strings, groupB, dest
-      let dist = dest.len - toPatch
-      assert dist > 0
-      dest[toPatch].span = dist
-
-proc toProblemDescription(c: var ProcCon; n: PNode): (seq[Key], LabelId) =
-  result = (@[], newLabels(c.labelGen, n.len))
-  assert n.kind == nkCaseStmt
-  for i in 1..<n.len:
-    let it = n[i]
-    let thisBranch = LabelId(result[1].int + i - 1)
-    if it.kind == nkOfBranch:
-      for j in 0..<it.len-1:
-        assert it[j].kind in {nkStrLit..nkTripleStrLit}
-        result[0].add (c.lit.strings.getOrIncl(it[j].strVal), thisBranch)
-
-proc decodeSolution(c: var ProcCon; dest: var Tree; s: seq[SearchResult]; i: int;
-                    selector: Value; info: PackedLineInfo) =
-  case s[i].kind
-  of SplitSearch:
-    let thenA = i+1
-    let elseA = thenA + (if s[thenA].kind == LinearSearch: 1 else: s[thenA].span)
-    let best = s[i].best
-
-    let tmp = getTemp(c, Bool8Id, info)
-    buildTyped dest, info, Asgn, Bool8Id:
-      dest.copyTree tmp
-      buildTyped dest, info, Call, Bool8Id:
-        c.addUseCodegenProc dest, "nimStrAtLe", info
-        dest.copyTree selector
-        dest.addIntVal c.lit.numbers, info, c.m.nativeIntId, best[1]
-        dest.addIntVal c.lit.numbers, info, Char8Id, best[0].int
-
-    template then() =
-      c.decodeSolution dest, s, thenA, selector, info
-    template otherwise() =
-      c.decodeSolution dest, s, elseA, selector, info
-    buildIfThenElse tmp, then, otherwise
-    freeTemp c, tmp
-
-  of LinearSearch:
-    let tmp = getTemp(c, Bool8Id, info)
-    for x in s[i].a:
-      buildTyped dest, info, Asgn, Bool8Id:
-        dest.copyTree tmp
-        buildTyped dest, info, Call, Bool8Id:
-          c.addUseCodegenProc dest, "eqStrings", info
-          dest.copyTree selector
-          dest.addStrLit info, x[0]
-      buildIf tmp:
-        c.code.gotoLabel info, Goto, x[1]
-    freeTemp c, tmp
-
-proc genStringCase(c: var ProcCon; n: PNode; d: var Value) =
-  let (problem, firstBranch) = toProblemDescription(c, n)
-  var solution: seq[SearchResult] = @[]
-  split c.lit.strings, problem, solution
-
-  # XXX Todo move complex case selector into a temporary.
-  let selector = c.genx(n[0])
-
-  let info = toLineInfo(c, n.info)
-  decodeSolution c, c.code, solution, 0, selector, info
-
-  let lend = newLabel(c.labelGen)
-  c.code.addLabel info, Goto, lend
-  for i in 1..<n.len:
-    let it = n[i]
-    let thisBranch = LabelId(firstBranch.int + i - 1)
-    c.code.addLabel info, Label, thisBranch
-    if it.kind == nkOfBranch:
-      gen(c, it.lastSon, d)
-      c.code.addLabel info, Goto, lend
-    else:
-      gen(c, it.lastSon, d)
-
-  c.code.addLabel info, Label, lend
-  freeTemp c, selector
diff --git a/compiler/nir/types2ir.nim b/compiler/nir/types2ir.nim
deleted file mode 100644
index 8d9583486..000000000
--- a/compiler/nir/types2ir.nim
+++ /dev/null
@@ -1,525 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2023 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-import std / [assertions, tables, sets]
-import ".." / [ast, types, options, sighashes, modulegraphs]
-import nirtypes
-
-type
-  TypesCon* = object
-    processed: Table[ItemId, TypeId]
-    processedByName: Table[string, TypeId]
-    recursionCheck: HashSet[ItemId]
-    conf: ConfigRef
-    stringType: TypeId
-
-proc initTypesCon*(conf: ConfigRef): TypesCon =
-  TypesCon(conf: conf, stringType: TypeId(-1))
-
-proc mangle(c: var TypesCon; t: PType): string =
-  result = $sighashes.hashType(t, c.conf)
-
-template cached(c: var TypesCon; t: PType; body: untyped) =
-  result = c.processed.getOrDefault(t.itemId)
-  if result.int == 0:
-    body
-    c.processed[t.itemId] = result
-
-template cachedByName(c: var TypesCon; t: PType; body: untyped) =
-  let key = mangle(c, t)
-  result = c.processedByName.getOrDefault(key)
-  if result.int == 0:
-    body
-    c.processedByName[key] = result
-
-proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId
-
-proc collectFieldTypes(c: var TypesCon; g: var TypeGraph; n: PNode; dest: var Table[ItemId, TypeId]) =
-  case n.kind
-  of nkRecList:
-    for i in 0..<n.len:
-      collectFieldTypes(c, g, n[i], dest)
-  of nkRecCase:
-    assert(n[0].kind == nkSym)
-    collectFieldTypes(c, g, n[0], dest)
-    for i in 1..<n.len:
-      case n[i].kind
-      of nkOfBranch, nkElse:
-        collectFieldTypes c, g, lastSon(n[i]), dest
-      else: discard
-  of nkSym:
-    dest[n.sym.itemId] = typeToIr(c, g, n.sym.typ)
-  else:
-    assert false, "unknown node kind: " & $n.kind
-
-proc objectToIr(c: var TypesCon; g: var TypeGraph; n: PNode; fieldTypes: Table[ItemId, TypeId]; unionId: var int) =
-  case n.kind
-  of nkRecList:
-    for i in 0..<n.len:
-      objectToIr(c, g, n[i], fieldTypes, unionId)
-  of nkRecCase:
-    assert(n[0].kind == nkSym)
-    objectToIr(c, g, n[0], fieldTypes, unionId)
-    let u = openType(g, UnionDecl)
-    g.addName "u_" & $unionId
-    inc unionId
-    for i in 1..<n.len:
-      case n[i].kind
-      of nkOfBranch, nkElse:
-        let subObj = openType(g, ObjectDecl)
-        g.addName "uo_" & $unionId & "_" & $i
-        objectToIr c, g, lastSon(n[i]), fieldTypes, unionId
-        sealType(g, subObj)
-      else: discard
-    sealType(g, u)
-  of nkSym:
-    g.addField n.sym.name.s & "_" & $n.sym.position, fieldTypes[n.sym.itemId], n.sym.offset
-  else:
-    assert false, "unknown node kind: " & $n.kind
-
-proc objectToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
-  if t.baseClass != nil:
-    # ensure we emitted the base type:
-    discard typeToIr(c, g, t.baseClass)
-
-  var unionId = 0
-  var fieldTypes = initTable[ItemId, TypeId]()
-  collectFieldTypes c, g, t.n, fieldTypes
-  let obj = openType(g, ObjectDecl)
-  g.addName mangle(c, t)
-  g.addSize c.conf.getSize(t)
-  g.addAlign c.conf.getAlign(t)
-
-  if t.baseClass != nil:
-    g.addNominalType(ObjectTy, mangle(c, t.baseClass))
-  else:
-    g.addBuiltinType VoidId # object does not inherit
-    if not lacksMTypeField(t):
-      let f2 = g.openType FieldDecl
-      let voidPtr = openType(g, APtrTy)
-      g.addBuiltinType(VoidId)
-      sealType(g, voidPtr)
-      g.addOffset 0 # type field is always at offset 0
-      g.addName "m_type"
-      sealType(g, f2) # FieldDecl
-
-  objectToIr c, g, t.n, fieldTypes, unionId
-  result = finishType(g, obj)
-
-proc objectHeaderToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
-  result = g.nominalType(ObjectTy, mangle(c, t))
-
-proc tupleToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
-  var fieldTypes = newSeq[TypeId](t.len)
-  for i in 0..<t.len:
-    fieldTypes[i] = typeToIr(c, g, t[i])
-  let obj = openType(g, ObjectDecl)
-  g.addName mangle(c, t)
-  g.addSize c.conf.getSize(t)
-  g.addAlign c.conf.getAlign(t)
-
-  var accum = OffsetAccum(maxAlign: 1)
-  for i in 0..<t.len:
-    let child = t[i]
-    g.addField "f_" & $i, fieldTypes[i], accum.offset
-
-    computeSizeAlign(c.conf, child)
-    accum.align(child.align)
-    accum.inc(int32(child.size))
-  result = finishType(g, obj)
-
-proc procToIr(c: var TypesCon; g: var TypeGraph; t: PType; addEnv = false): TypeId =
-  var fieldTypes = newSeq[TypeId](0)
-  for i in 0..<t.len:
-    if t[i] == nil or not isCompileTimeOnly(t[i]):
-      fieldTypes.add typeToIr(c, g, t[i])
-  let obj = openType(g, ProcTy)
-
-  case t.callConv
-  of ccNimCall, ccFastCall, ccClosure: g.addAnnotation "__fastcall"
-  of ccStdCall: g.addAnnotation "__stdcall"
-  of ccCDecl: g.addAnnotation "__cdecl"
-  of ccSafeCall: g.addAnnotation "__safecall"
-  of ccSysCall: g.addAnnotation "__syscall"
-  of ccInline: g.addAnnotation "__inline"
-  of ccNoInline: g.addAnnotation "__noinline"
-  of ccThisCall: g.addAnnotation "__thiscall"
-  of ccNoConvention, ccMember: g.addAnnotation ""
-
-  for i in 0..<fieldTypes.len:
-    g.addType fieldTypes[i]
-
-  if addEnv:
-    let a = openType(g, APtrTy)
-    g.addBuiltinType(VoidId)
-    sealType(g, a)
-
-  if tfVarargs in t.flags:
-    g.addVarargs()
-  result = finishType(g, obj)
-
-proc nativeInt(c: TypesCon): TypeId =
-  case c.conf.target.intSize
-  of 2: result = Int16Id
-  of 4: result = Int32Id
-  else: result = Int64Id
-
-proc openArrayPayloadType*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
-  let e = elementType(t)
-  let elementType = typeToIr(c, g, e)
-  let arr = g.openType AArrayPtrTy
-  g.addType elementType
-  result = finishType(g, arr) # LastArrayTy
-
-proc openArrayToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
-  # object (a: ArrayPtr[T], len: int)
-  let e = elementType(t)
-  let mangledBase = mangle(c, e)
-  let typeName = "NimOpenArray" & mangledBase
-
-  let elementType = typeToIr(c, g, e)
-  #assert elementType.int >= 0, typeToString(t)
-
-  let p = openType(g, ObjectDecl)
-  g.addName typeName
-  g.addSize c.conf.target.ptrSize*2
-  g.addAlign c.conf.target.ptrSize
-
-  let f = g.openType FieldDecl
-  let arr = g.openType AArrayPtrTy
-  g.addType elementType
-  sealType(g, arr) # LastArrayTy
-  g.addOffset 0
-  g.addName "data"
-  sealType(g, f) # FieldDecl
-
-  g.addField "len", c.nativeInt, c.conf.target.ptrSize
-
-  result = finishType(g, p) # ObjectDecl
-
-proc strPayloadType(c: var TypesCon; g: var TypeGraph): (string, TypeId) =
-  result = ("NimStrPayload", TypeId(-1))
-  let p = openType(g, ObjectDecl)
-  g.addName result[0]
-  g.addSize c.conf.target.ptrSize*2
-  g.addAlign c.conf.target.ptrSize
-
-  g.addField "cap", c.nativeInt, 0
-
-  let f = g.openType FieldDecl
-  let arr = g.openType LastArrayTy
-  g.addBuiltinType Char8Id
-  result[1] = finishType(g, arr) # LastArrayTy
-  g.addOffset c.conf.target.ptrSize # comes after the len field
-  g.addName "data"
-  sealType(g, f) # FieldDecl
-
-  sealType(g, p)
-
-proc strPayloadPtrType*(c: var TypesCon; g: var TypeGraph): (TypeId, TypeId) =
-  let (mangled, arrayType) = strPayloadType(c, g)
-  let ffp = g.openType APtrTy
-  g.addNominalType ObjectTy, mangled
-  result = (finishType(g, ffp), arrayType) # APtrTy
-
-proc stringToIr(c: var TypesCon; g: var TypeGraph): TypeId =
-  #[
-
-    NimStrPayload = object
-      cap: int
-      data: UncheckedArray[char]
-
-    NimStringV2 = object
-      len: int
-      p: ptr NimStrPayload
-
-  ]#
-  let payload = strPayloadType(c, g)
-
-  let str = openType(g, ObjectDecl)
-  g.addName "NimStringV2"
-  g.addSize c.conf.target.ptrSize*2
-  g.addAlign c.conf.target.ptrSize
-
-  g.addField "len", c.nativeInt, 0
-
-  let fp = g.openType FieldDecl
-  let ffp = g.openType APtrTy
-  g.addNominalType ObjectTy, "NimStrPayload"
-  sealType(g, ffp) # APtrTy
-  g.addOffset c.conf.target.ptrSize # comes after 'len' field
-  g.addName "p"
-  sealType(g, fp) # FieldDecl
-
-  result = finishType(g, str) # ObjectDecl
-
-proc seqPayloadType(c: var TypesCon; g: var TypeGraph; t: PType): (string, TypeId) =
-  #[
-    NimSeqPayload[T] = object
-      cap: int
-      data: UncheckedArray[T]
-  ]#
-  let e = elementType(t)
-  result = (mangle(c, e), TypeId(-1))
-  let payloadName = "NimSeqPayload" & result[0]
-
-  let elementType = typeToIr(c, g, e)
-
-  let p = openType(g, ObjectDecl)
-  g.addName payloadName
-  g.addSize c.conf.target.intSize
-  g.addAlign c.conf.target.intSize
-
-  g.addField "cap", c.nativeInt, 0
-
-  let f = g.openType FieldDecl
-  let arr = g.openType LastArrayTy
-  g.addType elementType
-  # DO NOT USE `finishType` here as it is an inner type. This is subtle and we
-  # probably need an even better API here.
-  sealType(g, arr)
-  result[1] = TypeId(arr)
-
-  g.addOffset c.conf.target.ptrSize
-  g.addName "data"
-  sealType(g, f) # FieldDecl
-
-  sealType(g, p)
-
-proc seqPayloadPtrType*(c: var TypesCon; g: var TypeGraph; t: PType): (TypeId, TypeId) =
-  let (mangledBase, arrayType) = seqPayloadType(c, g, t)
-  let ffp = g.openType APtrTy
-  g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase
-  result = (finishType(g, ffp), arrayType) # APtrTy
-
-proc seqToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
-  #[
-    NimSeqV2*[T] = object
-      len: int
-      p: ptr NimSeqPayload[T]
-  ]#
-  let (mangledBase, _) = seqPayloadType(c, g, t)
-
-  let sq = openType(g, ObjectDecl)
-  g.addName "NimSeqV2" & mangledBase
-  g.addSize c.conf.getSize(t)
-  g.addAlign c.conf.getAlign(t)
-
-  g.addField "len", c.nativeInt, 0
-
-  let fp = g.openType FieldDecl
-  let ffp = g.openType APtrTy
-  g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase
-  sealType(g, ffp) # APtrTy
-  g.addOffset c.conf.target.ptrSize
-  g.addName "p"
-  sealType(g, fp) # FieldDecl
-
-  result = finishType(g, sq) # ObjectDecl
-
-
-proc closureToIr(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
-  # struct {fn(args, void* env), env}
-  # typedef struct {$n" &
-  #        "N_NIMCALL_PTR($2, ClP_0) $3;$n" &
-  #        "void* ClE_0;$n} $1;$n"
-  let mangledBase = mangle(c, t)
-  let typeName = "NimClosure" & mangledBase
-
-  let procType = procToIr(c, g, t, addEnv=true)
-
-  let p = openType(g, ObjectDecl)
-  g.addName typeName
-  g.addSize c.conf.getSize(t)
-  g.addAlign c.conf.getAlign(t)
-
-  let f = g.openType FieldDecl
-  g.addType procType
-  g.addOffset 0
-  g.addName "ClP_0"
-  sealType(g, f) # FieldDecl
-
-  let f2 = g.openType FieldDecl
-  let voidPtr = openType(g, APtrTy)
-  g.addBuiltinType(VoidId)
-  sealType(g, voidPtr)
-
-  g.addOffset c.conf.target.ptrSize
-  g.addName "ClE_0"
-  sealType(g, f2) # FieldDecl
-
-  result = finishType(g, p) # ObjectDecl
-
-proc bitsetBasetype*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
-  let s = int(getSize(c.conf, t))
-  case s
-  of 1: result = UInt8Id
-  of 2: result = UInt16Id
-  of 4: result = UInt32Id
-  of 8: result = UInt64Id
-  else: result = UInt8Id
-
-proc typeToIr*(c: var TypesCon; g: var TypeGraph; t: PType): TypeId =
-  if t == nil: return VoidId
-  case t.kind
-  of tyInt:
-    case int(getSize(c.conf, t))
-    of 2: result = Int16Id
-    of 4: result = Int32Id
-    else: result = Int64Id
-  of tyInt8: result = Int8Id
-  of tyInt16: result = Int16Id
-  of tyInt32: result = Int32Id
-  of tyInt64: result = Int64Id
-  of tyFloat:
-    case int(getSize(c.conf, t))
-    of 4: result = Float32Id
-    else: result = Float64Id
-  of tyFloat32: result = Float32Id
-  of tyFloat64: result = Float64Id
-  of tyFloat128: result = getFloat128Type(g)
-  of tyUInt:
-    case int(getSize(c.conf, t))
-    of 2: result = UInt16Id
-    of 4: result = UInt32Id
-    else: result = UInt64Id
-  of tyUInt8: result = UInt8Id
-  of tyUInt16: result = UInt16Id
-  of tyUInt32: result = UInt32Id
-  of tyUInt64: result = UInt64Id
-  of tyBool: result = Bool8Id
-  of tyChar: result = Char8Id
-  of tyVoid: result = VoidId
-  of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned, tyRange:
-    result = typeToIr(c, g, t.skipModifier)
-  of tyEnum:
-    if firstOrd(c.conf, t) < 0:
-      result = Int32Id
-    else:
-      case int(getSize(c.conf, t))
-      of 1: result = UInt8Id
-      of 2: result = UInt16Id
-      of 4: result = Int32Id
-      of 8: result = Int64Id
-      else: result = Int32Id
-  of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic:
-    if t.len > 0:
-      result = typeToIr(c, g, t.skipModifier)
-    else:
-      result = TypeId(-1)
-  of tyFromExpr:
-    if t.n != nil and t.n.typ != nil:
-      result = typeToIr(c, g, t.n.typ)
-    else:
-      result = TypeId(-1)
-  of tyArray:
-    cached(c, t):
-      var n = toInt64(lengthOrd(c.conf, t))
-      if n <= 0: n = 1   # make an array of at least one element
-      let elemType = typeToIr(c, g, t.elementType)
-      let a = openType(g, ArrayTy)
-      g.addType(elemType)
-      g.addArrayLen n
-      g.addName mangle(c, t)
-      result = finishType(g, a)
-  of tyPtr, tyRef:
-    cached(c, t):
-      let e = t.elementType
-      if e.kind == tyUncheckedArray:
-        let elemType = typeToIr(c, g, e.elementType)
-        let a = openType(g, AArrayPtrTy)
-        g.addType(elemType)
-        result = finishType(g, a)
-      else:
-        let elemType = typeToIr(c, g, t.elementType)
-        let a = openType(g, APtrTy)
-        g.addType(elemType)
-        result = finishType(g, a)
-  of tyVar, tyLent:
-    cached(c, t):
-      let e = t.elementType
-      if e.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
-        # skip the modifier, `var openArray` is a (ptr, len) pair too:
-        result = typeToIr(c, g, e)
-      else:
-        let elemType = typeToIr(c, g, e)
-        let a = openType(g, APtrTy)
-        g.addType(elemType)
-        result = finishType(g, a)
-  of tySet:
-    let s = int(getSize(c.conf, t))
-    case s
-    of 1: result = UInt8Id
-    of 2: result = UInt16Id
-    of 4: result = UInt32Id
-    of 8: result = UInt64Id
-    else:
-      # array[U8, s]
-      cached(c, t):
-        let a = openType(g, ArrayTy)
-        g.addType(UInt8Id)
-        g.addArrayLen s
-        g.addName mangle(c, t)
-        result = finishType(g, a)
-  of tyPointer, tyNil:
-    # tyNil can happen for code like: `const CRAP = nil` which we have in posix.nim
-    let a = openType(g, APtrTy)
-    g.addBuiltinType(VoidId)
-    result = finishType(g, a)
-  of tyObject:
-    # Objects are special as they can be recursive in Nim. This is easily solvable.
-    # We check if we are already "processing" t. If so, we produce `ObjectTy`
-    # instead of `ObjectDecl`.
-    cached(c, t):
-      if not c.recursionCheck.containsOrIncl(t.itemId):
-        result = objectToIr(c, g, t)
-      else:
-        result = objectHeaderToIr(c, g, t)
-  of tyTuple:
-    cachedByName(c, t):
-      result = tupleToIr(c, g, t)
-  of tyProc:
-    cached(c, t):
-      if t.callConv == ccClosure:
-        result = closureToIr(c, g, t)
-      else:
-        result = procToIr(c, g, t)
-  of tyVarargs, tyOpenArray:
-    cached(c, t):
-      result = openArrayToIr(c, g, t)
-  of tyString:
-    if c.stringType.int < 0:
-      result = stringToIr(c, g)
-      c.stringType = result
-    else:
-      result = c.stringType
-  of tySequence:
-    cachedByName(c, t):
-      result = seqToIr(c, g, t)
-  of tyCstring:
-    cached(c, t):
-      let a = openType(g, AArrayPtrTy)
-      g.addBuiltinType Char8Id
-      result = finishType(g, a)
-  of tyUncheckedArray:
-    # We already handled the `ptr UncheckedArray` in a special way.
-    cached(c, t):
-      let elemType = typeToIr(c, g, t.elementType)
-      let a = openType(g, LastArrayTy)
-      g.addType(elemType)
-      result = finishType(g, a)
-  of tyUntyped, tyTyped:
-    # this avoids a special case for system.echo which is not a generic but
-    # uses `varargs[typed]`:
-    result = VoidId
-  of tyNone, tyEmpty, tyTypeDesc,
-     tyGenericInvocation, tyProxy, tyBuiltInTypeClass,
-     tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass,
-     tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward:
-    result = TypeId(-1)
diff --git a/compiler/nodekinds.nim b/compiler/nodekinds.nim
index 98ae9405d..ccdbbd26d 100644
--- a/compiler/nodekinds.nim
+++ b/compiler/nodekinds.nim
@@ -204,6 +204,7 @@ type
     nkModuleRef           # for .rod file support: A (moduleId, itemId) pair
     nkReplayAction        # for .rod file support: A replay action
     nkNilRodNode          # for .rod file support: a 'nil' PNode
+    nkOpenSym             # container for captured sym that can be overriden by local symbols
 
 const
   nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
diff --git a/compiler/options.nim b/compiler/options.nim
index 356aa6cc8..b77bdd2a3 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -139,7 +139,6 @@ type
     backendCpp = "cpp"
     backendJs = "js"
     backendObjc = "objc"
-    backendNir = "nir"
     # backendNimscript = "nimscript" # this could actually work
     # backendLlvm = "llvm" # probably not well supported; was cmdCompileToLLVM
 
@@ -147,7 +146,6 @@ type
     cmdNone # not yet processed command
     cmdUnknown # command unmapped
     cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS,
-    cmdCompileToNir,
     cmdCrun # compile and run in nimache
     cmdTcc # run the project via TCC backend
     cmdCheck # semantic checking for whole project
@@ -176,12 +174,11 @@ type
 
 const
   cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
-                  cmdCompileToJS, cmdCrun, cmdCompileToNir}
+                  cmdCompileToJS, cmdCrun}
   cmdDocLike* = {cmdDoc0, cmdDoc, cmdDoc2tex, cmdJsondoc0, cmdJsondoc,
                  cmdCtags, cmdBuildindex}
 
 type
-  NimVer* = tuple[major: int, minor: int, patch: int]
   TStringSeq* = seq[string]
   TGCMode* = enum             # the selected GC
     gcUnselected = "unselected"
@@ -228,7 +225,9 @@ type
     strictDefs,
     strictCaseObjects,
     inferGenericTypes,
-    genericsOpenSym,
+    openSym, # remove nfDisabledOpenSym when this is default
+    # alternative to above:
+    genericsOpenSym
     vtables
 
   LegacyFeature* = enum
@@ -246,13 +245,15 @@ type
     emitGenerics
       ## generics are emitted in the module that contains them.
       ## Useful for libraries that rely on local passC
+    jsNoLambdaLifting
+      ## Old transformation for closures in JS backend
 
   SymbolFilesOption* = enum
     disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest
 
   TSystemCC* = enum
     ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccBcc, ccVcc,
-    ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl
+    ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl, ccHipcc, ccNvcc
 
   ExceptionSystem* = enum
     excNone,   # no exception system selected yet
@@ -368,7 +369,6 @@ type
     arguments*: string ## the arguments to be passed to the program that
                        ## should be run
     ideCmd*: IdeCmd
-    oldNewlines*: bool
     cCompiler*: TSystemCC # the used compiler
     modifiedyNotes*: TNoteKinds # notes that have been set/unset from either cmdline/configs
     cmdlineNotes*: TNoteKinds # notes that have been set/unset from cmdline
@@ -395,8 +395,7 @@ type
     outDir*: AbsoluteDir
     jsonBuildFile*: AbsoluteFile
     prefixDir*, libpath*, nimcacheDir*: AbsoluteDir
-    nimStdlibVersion*: NimVer
-    dllOverrides, moduleOverrides*, cfileSpecificOptions*: StringTableRef
+    dllOverrides*, moduleOverrides*, cfileSpecificOptions*: StringTableRef
     projectName*: string # holds a name like 'nim'
     projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/
     projectFull*: AbsoluteFile # projectPath/projectName
@@ -408,7 +407,6 @@ type
     commandArgs*: seq[string] # any arguments after the main command
     commandLine*: string
     extraCmds*: seq[string] # for writeJsonBuildInstructions
-    keepComments*: bool # whether the parser needs to keep comments
     implicitImports*: seq[string] # modules that are to be implicitly imported
     implicitIncludes*: seq[string] # modules that are to be implicitly included
     docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \
@@ -449,16 +447,6 @@ type
     clientProcessId*: int
 
 
-proc parseNimVersion*(a: string): NimVer =
-  # could be moved somewhere reusable
-  result = default(NimVer)
-  if a.len > 0:
-    let b = a.split(".")
-    assert b.len == 3, a
-    template fn(i) = result[i] = b[i].parseInt # could be optimized if needed
-    fn(0)
-    fn(1)
-    fn(2)
 
 proc assignIfDefault*[T](result: var T, val: T, def = default(T)) =
   ## if `result` was already assigned to a value (that wasn't `def`), this is a noop.
@@ -592,7 +580,6 @@ proc newConfigRef*(): ConfigRef =
     command: "", # the main command (e.g. cc, check, scan, etc)
     commandArgs: @[], # any arguments after the main command
     commandLine: "",
-    keepComments: true, # whether the parser needs to keep comments
     implicitImports: @[], # modules that are to be implicitly imported
     implicitIncludes: @[], # modules that are to be implicitly included
     docSeeSrcUrl: "",
@@ -633,12 +620,6 @@ proc newPartialConfigRef*(): ConfigRef =
 proc cppDefine*(c: ConfigRef; define: string) =
   c.cppDefines.incl define
 
-proc getStdlibVersion*(conf: ConfigRef): NimVer =
-  if conf.nimStdlibVersion == (0,0,0):
-    let s = conf.symbols.getOrDefault("nimVersion", "")
-    conf.nimStdlibVersion = s.parseNimVersion
-  result = conf.nimStdlibVersion
-
 proc isDefined*(conf: ConfigRef; symbol: string): bool =
   if conf.symbols.hasKey(symbol):
     result = true
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim
index 84c2980c4..e8ec22fe1 100644
--- a/compiler/parampatterns.nim
+++ b/compiler/parampatterns.nim
@@ -266,7 +266,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
     if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in
         {tyOpenArray, tyTuple, tyObject}:
       result = isAssignable(owner, n[1])
-    elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct):
+    elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct, {IgnoreRangeShallow}):
       # types that are equal modulo distinction preserve l-value:
       result = isAssignable(owner, n[1])
   of nkHiddenDeref:
@@ -283,8 +283,15 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
   of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
     result = isAssignable(owner, n[0])
   of nkCallKinds:
-    # builtin slice keeps lvalue-ness:
-    if getMagic(n) in {mArrGet, mSlice}:
+    let m = getMagic(n)
+    if m == mSlice:
+      # builtin slice keeps l-value-ness
+      # except for pointers because slice dereferences
+      if n[1].typ.kind == tyPtr:
+        result = arLValue
+      else:
+        result = isAssignable(owner, n[1])
+    elif m == mArrGet:
       result = isAssignable(owner, n[1])
     elif n.typ != nil:
       case n.typ.kind
diff --git a/compiler/parser.nim b/compiler/parser.nim
index eb7b8c289..747505097 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -1401,7 +1401,7 @@ proc primary(p: var Parser, mode: PrimaryMode): PNode =
     result = primarySuffix(p, result, baseInd, mode)
 
 proc binaryNot(p: var Parser; a: PNode): PNode =
-  if p.tok.tokType == tkNot:
+  if p.tok.tokType == tkNot and p.tok.indent < 0:
     let notOpr = newIdentNodeP(p.tok.ident, p)
     getTok(p)
     optInd(p, notOpr)
diff --git a/compiler/pipelines.nim b/compiler/pipelines.nim
index a44edbb7f..55e7fe892 100644
--- a/compiler/pipelines.nim
+++ b/compiler/pipelines.nim
@@ -13,7 +13,6 @@ when not defined(leanCompiler):
 import std/[syncio, objectdollar, assertions, tables, strutils, strtabs]
 import renderer
 import ic/replayer
-import nir/nir
 
 proc setPipeLinePass*(graph: ModuleGraph; pass: PipelinePass) =
   graph.pipelinePass = pass
@@ -45,10 +44,6 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext):
       result = nil
   of EvalPass, InterpreterPass:
     result = interpreterCode(bModule, semNode)
-  of NirReplPass:
-    result = runCode(bModule, semNode)
-  of NirPass:
-    result = nirBackend(bModule, semNode)
   of NonePass:
     raiseAssert "use setPipeLinePass to set a proper PipelinePass"
 
@@ -111,8 +106,6 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
     case graph.pipelinePass
     of CgenPass:
       setupCgen(graph, module, idgen)
-    of NirPass:
-      openNirBackend(graph, module, idgen)
     of JSgenPass:
       when not defined(leanCompiler):
         setupJSgen(graph, module, idgen)
@@ -120,8 +113,6 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
         nil
     of EvalPass, InterpreterPass:
       setupEvalGen(graph, module, idgen)
-    of NirReplPass:
-      setupNirReplGen(graph, module, idgen)
     of GenDependPass:
       setupDependPass(graph, module, idgen)
     of Docgen2Pass:
@@ -209,10 +200,6 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
       discard finalJSCodeGen(graph, bModule, finalNode)
   of EvalPass, InterpreterPass:
     discard interpreterCode(bModule, finalNode)
-  of NirReplPass:
-    discard runCode(bModule, finalNode)
-  of NirPass:
-    closeNirBackend(bModule, finalNode)
   of SemPass, GenDependPass:
     discard
   of Docgen2Pass, Docgen2TexPass:
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index a644639fe..9a298cd90 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -81,14 +81,14 @@ const
     wRequiresInit, wNoalias, wAlign, wNoInit} - {wExportNims, wNodecl} # why exclude these?
   varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar,
     wMagic, wHeader, wCompilerProc, wCore, wDynlib,
-    wNoInit, wCompileTime, wGlobal,
+    wNoInit, wCompileTime, wGlobal, wLiftLocals,
     wGensym, wInject, wCodegenDecl,
     wGuard, wGoto, wCursor, wNoalias, wAlign}
   constPragmas* = declPragmas + {wHeader, wMagic,
     wGensym, wInject,
     wIntDefine, wStrDefine, wBoolDefine, wDefine,
     wCompilerProc, wCore}
-  paramPragmas* = {wNoalias, wInject, wGensym, wByRef, wByCopy, wCodegenDecl}
+  paramPragmas* = {wNoalias, wInject, wGensym, wByRef, wByCopy, wCodegenDecl, wExportc, wExportCpp}
   letPragmas* = varPragmas
   procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect,
                       wThread, wRaises, wEffectsOf, wLocks, wTags, wForbids, wGcSafe,
@@ -156,16 +156,16 @@ proc pragmaEnsures(c: PContext, n: PNode) =
 proc setExternName(c: PContext; s: PSym, extname: string, info: TLineInfo) =
   # special cases to improve performance:
   if extname == "$1":
-    s.loc.r = rope(s.name.s)
+    s.loc.snippet = rope(s.name.s)
   elif '$' notin extname:
-    s.loc.r = rope(extname)
+    s.loc.snippet = rope(extname)
   else:
     try:
-      s.loc.r = rope(extname % s.name.s)
+      s.loc.snippet = rope(extname % s.name.s)
     except ValueError:
       localError(c.config, info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)")
   when hasFFI:
-    s.cname = $s.loc.r
+    s.cname = $s.loc.snippet
 
 
 proc makeExternImport(c: PContext; s: PSym, extname: string, info: TLineInfo) =
@@ -370,7 +370,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) =
 proc processNote(c: PContext, n: PNode) =
   template handleNote(enumVals, notes) =
     let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown)
-    if x !=  errUnknown:
+    if x != errUnknown:
       nk = TNoteKind(x)
       let x = c.semConstBoolExpr(c, n[1])
       n[1] = x
@@ -480,6 +480,18 @@ proc processOption(c: PContext, n: PNode, resOptions: var TOptions) =
     # calling conventions (boring...):
     localError(c.config, n.info, "option expected")
 
+proc checkPushedPragma(c: PContext, n: PNode) =
+  let keyDeep = n.kind in nkPragmaCallKinds and n.len > 1
+  var key = if keyDeep: n[0] else: n
+  if key.kind in nkIdentKinds:
+    let ident = considerQuotedIdent(c, key)
+    var userPragma = strTableGet(c.userPragmas, ident)
+    if userPragma == nil:
+      let k = whichKeyword(ident)
+      # TODO: might as well make a list which is not accepted by `push`: emit, cast etc.
+      if k == wEmit:
+        localError(c.config, n.info, "an 'emit' pragma cannot be pushed")
+
 proc processPush(c: PContext, n: PNode, start: int) =
   if n[start-1].kind in nkPragmaCallKinds:
     localError(c.config, n.info, "'push' cannot have arguments")
@@ -487,6 +499,7 @@ proc processPush(c: PContext, n: PNode, start: int) =
   for i in start..<n.len:
     if not tryProcessOption(c, n[i], c.config.options):
       # simply store it somewhere:
+      checkPushedPragma(c, n[i])
       if x.otherPragmas.isNil:
         x.otherPragmas = newNodeI(nkPragma, n.info)
       x.otherPragmas.add n[i]
@@ -787,13 +800,14 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
 proc semCustomPragma(c: PContext, n: PNode, sym: PSym): PNode =
   var callNode: PNode
 
-  if n.kind in {nkIdent, nkSym}:
+  case n.kind
+  of nkIdentKinds:
     # pragma -> pragma()
     callNode = newTree(nkCall, n)
-  elif n.kind == nkExprColonExpr:
+  of nkExprColonExpr:
     # pragma: arg -> pragma(arg)
     callNode = newTree(nkCall, n[0], n[1])
-  elif n.kind in nkPragmaCallKinds:
+  of nkPragmaCallKinds - {nkExprColonExpr}:
     callNode = n
   else:
     invalidPragma(c, n)
@@ -869,6 +883,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
 
     # number of pragmas increase/decrease with user pragma expansion
     inc c.instCounter
+    defer: dec c.instCounter
     if c.instCounter > 100:
       globalError(c.config, it.info, "recursive dependency: " & userPragma.name.s)
 
@@ -878,7 +893,6 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
     pragma(c, sym, userPragma.ast, validPragmas, isStatement)
     n.sons[i..i] = userPragma.ast.sons # expand user pragma with its content
     i.inc(userPragma.ast.len - 1) # inc by -1 is ok, user pragmas was empty
-    dec c.instCounter
   else:
     let k = whichKeyword(ident)
     if k in validPragmas:
@@ -1001,7 +1015,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         incl(sym.loc.flags, lfHeader)
         incl(sym.loc.flags, lfNoDecl)
         # implies nodecl, because otherwise header would not make sense
-        if sym.loc.r == "": sym.loc.r = rope(sym.name.s)
+        if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s)
       of wNoSideEffect:
         noVal(c, it)
         if sym != nil:
@@ -1295,7 +1309,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         noVal(c, it)
         if sym == nil: invalidPragma(c, it)
         else: sym.flags.incl sfUsed
-      of wLiftLocals: discard
+      of wLiftLocals:
+        sym.flags.incl(sfForceLift)
       of wRequires, wInvariant, wAssume, wAssert:
         pragmaProposition(c, it)
       of wEnsures:
@@ -1329,6 +1344,16 @@ proc mergePragmas(n, pragmas: PNode) =
   else:
     for p in pragmas: n[pragmasPos].add p
 
+proc mergeValidPragmas(n, pragmas: PNode, validPragmas: TSpecialWords) =
+  if n[pragmasPos].kind == nkEmpty:
+    n[pragmasPos] = newNodeI(nkPragma, n.info)
+  for p in pragmas:
+    let prag = whichPragma(p)
+    if prag in validPragmas:
+      let copy = copyTree(p)
+      overwriteLineInfo copy, n.info
+      n[pragmasPos].add copy
+
 proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
                       validPragmas: TSpecialWords) =
   if sym != nil and sym.kind != skModule:
@@ -1342,7 +1367,8 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
             internalError(c.config, info, "implicitPragmas")
           inc i
         popInfoContext(c.config)
-        if sym.kind in routineKinds and sym.ast != nil: mergePragmas(sym.ast, o)
+        if sym.kind in routineKinds and sym.ast != nil:
+          mergeValidPragmas(sym.ast, o, validPragmas)
 
     if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
       localError(c.config, info, ".dynlib requires .exportc")
@@ -1351,7 +1377,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
         sfImportc in sym.flags and lib != nil:
       incl(sym.loc.flags, lfDynamicLib)
       addToLib(lib, sym)
-      if sym.loc.r == "": sym.loc.r = rope(sym.name.s)
+      if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s)
 
 proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
   if n == nil: return false
diff --git a/compiler/pushpoppragmas.nim b/compiler/pushpoppragmas.nim
new file mode 100644
index 000000000..773e7013b
--- /dev/null
+++ b/compiler/pushpoppragmas.nim
@@ -0,0 +1,54 @@
+import pragmas, options, ast, trees, lineinfos, idents, wordrecg
+import std/assertions
+
+import renderer
+
+
+proc processNote(config: ConfigRef, n: PNode) =
+  template handleNote(enumVals, notes) =
+    let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown)
+    assert x != errUnknown
+    assert n[1].kind == nkIntLit
+
+    nk = TNoteKind(x)
+    if n[1].intVal != 0: incl(notes, nk)
+    else: excl(notes, nk)
+
+  var nk: TNoteKind
+  case whichKeyword(n[0][0].ident)
+  of wHint: handleNote(hintMin .. hintMax, config.notes)
+  of wWarning: handleNote(warnMin .. warnMax, config.notes)
+  of wWarningAsError: handleNote(warnMin .. warnMax, config.warningAsErrors)
+  of wHintAsError: handleNote(hintMin .. hintMax, config.warningAsErrors)
+  else: discard
+
+proc pushBackendOption(optionsStack: var seq[(TOptions, TNoteKinds)], options: TOptions, notes: TNoteKinds) =
+  optionsStack.add (options, notes)
+
+proc popBackendOption(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions) =
+  let entry = optionsStack[^1]
+  options = entry[0]
+  config.notes = entry[1]
+  optionsStack.setLen(optionsStack.len-1)
+
+proc processPushBackendOption*(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions,
+                           n: PNode, start: int) =
+  pushBackendOption(optionsStack, options, config.notes)
+  for i in start..<n.len:
+    let it = n[i]
+    if it.kind in nkPragmaCallKinds and it.len == 2:
+      if it[0].kind == nkBracketExpr and
+          it[0].len == 2 and
+          it[0][1].kind == nkIdent and it[0][0].kind == nkIdent:
+        processNote(config, it)
+      elif it[1].kind == nkIntLit:
+        let sw = whichPragma(it[0])
+        let opts = pragmaToOptions(sw)
+        if opts != {}:
+          if it[1].intVal != 0:
+            options.incl opts
+          else:
+            options.excl opts
+
+template processPopBackendOption*(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions) =
+  popBackendOption(config, optionsStack, options)
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 3a7c60953..cc07c0c2d 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -442,6 +442,11 @@ proc atom(g: TSrcGen; n: PNode): string =
       result = $n.floatVal & "\'f64"
     else:
       result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f64"
+  of nkFloat128Lit:
+    if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
+      result = $n.floatVal & "\'f128"
+    else:
+      result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f128"
   of nkNilLit: result = "nil"
   of nkType:
     if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s
@@ -514,6 +519,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
     result = if n.len > 0: lcomma(g, n) + 2 else: len("{:}")
   of nkClosedSymChoice, nkOpenSymChoice:
     if n.len > 0: result += lsub(g, n[0])
+  of nkOpenSym: result = lsub(g, n[0])
   of nkTupleTy: result = lcomma(g, n) + len("tuple[]")
   of nkTupleClassTy: result = len("tuple")
   of nkDotExpr: result = lsons(g, n) + 1
@@ -1013,7 +1019,7 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
 proc skipHiddenNodes(n: PNode): PNode =
   result = n
   while result != nil:
-    if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv} and result.len > 1:
+    if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv, nkOpenSym} and result.len > 1:
       result = result[1]
     elif result.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString} and
         result.len > 0:
@@ -1275,6 +1281,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
       put(g, tkParRi, if n.kind == nkOpenSymChoice: "|...)" else: ")")
     else:
       gsub(g, n, 0)
+  of nkOpenSym: gsub(g, n, 0)
   of nkPar, nkClosure:
     put(g, tkParLe, "(")
     gcomma(g, n, c)
diff --git a/compiler/reorder.nim b/compiler/reorder.nim
index 31c8befe2..2f7c04af1 100644
--- a/compiler/reorder.nim
+++ b/compiler/reorder.nim
@@ -322,6 +322,14 @@ proc intersects(s1, s2: IntSet): bool =
     if s2.contains(a):
       return true
 
+proc hasPushOrPopPragma(n: DepN): bool =
+  # Checks if the tree node has some pragmas that do not
+  # play well with reordering, like the push/pop pragma
+  # no crossing for push/pop barrier
+  let a = n.pnode
+  result = a.kind == nkPragma and a[0].kind == nkIdent and
+      (a[0].ident.s == "push" or a[0].ident.s == "pop")
+
 proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
   # Build a dependency graph
   result = newSeqOfCap[DepN](deps.len)
@@ -363,6 +371,13 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
             for dep in deps[i][0]:
               if dep in declares:
                 ni.expls.add "one declares \"" & idNames[dep] & "\" and the other defines it"
+      elif hasPushOrPopPragma(nj):
+        # Every node that comes after a push/pop pragma must
+        # depend on it; vice versa
+        if j < i:
+          ni.kids.add nj
+        else:
+          nj.kids.add ni
       else:
         for d in declares:
           if uses.contains(d):
@@ -403,18 +418,7 @@ proc getStrongComponents(g: var DepG): seq[seq[DepN]] =
     if v.idx < 0:
       strongConnect(v, idx, s, result)
 
-proc hasForbiddenPragma(n: PNode): bool =
-  # Checks if the tree node has some pragmas that do not
-  # play well with reordering, like the push/pop pragma
-  result = false
-  for a in n:
-    if a.kind == nkPragma and a[0].kind == nkIdent and
-        a[0].ident.s == "push":
-      return true
-
 proc reorder*(graph: ModuleGraph, n: PNode, module: PSym): PNode =
-  if n.hasForbiddenPragma:
-    return n
   var includedFiles = initIntSet()
   let mpath = toFullPath(graph.config, module.fileIdx)
   let n = expandIncludes(graph, module, n, mpath,
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 92c21aece..2cf93d365 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -21,7 +21,7 @@ import
   extccomp
 
 import vtables
-import std/[strtabs, math, tables, intsets, strutils]
+import std/[strtabs, math, tables, intsets, strutils, packedsets]
 
 when not defined(leanCompiler):
   import spawn
@@ -103,7 +103,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
     result = nil
     for ch in arg:
       if sameType(ch.typ, formal):
-        return getConstExpr(c.module, ch, c.idgen, c.graph)
+        return ch
     typeMismatch(c.config, info, formal, arg.typ, arg)
   else:
     result = indexTypesMatch(c, formal, arg.typ, arg)
@@ -222,66 +222,7 @@ proc shouldCheckCaseCovered(caseTyp: PType): bool =
   else:
     discard
 
-proc endsInNoReturn(n: PNode): bool =
-  ## check if expr ends the block like raising or call of noreturn procs do
-  result = false # assume it does return
-
-  template checkBranch(branch) =
-    if not endsInNoReturn(branch):
-      # proved a branch returns
-      return false
-
-  var it = n
-  # skip these beforehand, no special handling needed
-  while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
-    it = it.lastSon
-
-  case it.kind
-  of nkIfStmt:
-    var hasElse = false
-    for branch in it:
-      checkBranch:
-        if branch.len == 2:
-          branch[1]
-        elif branch.len == 1:
-          hasElse = true
-          branch[0]
-        else:
-          raiseAssert "Malformed `if` statement during endsInNoReturn"
-    # none of the branches returned
-    result = hasElse # Only truly a no-return when it's exhaustive
-  of nkCaseStmt:
-    let caseTyp = skipTypes(it[0].typ, abstractVar-{tyTypeDesc})
-    # semCase should already have checked for exhaustiveness in this case
-    # effectively the same as having an else
-    var hasElse = caseTyp.shouldCheckCaseCovered()
-
-    # actual noreturn checks
-    for i in 1 ..< it.len:
-      let branch = it[i]
-      checkBranch:
-        case branch.kind
-        of nkOfBranch:
-          branch[^1]
-        of nkElifBranch:
-          branch[1]
-        of nkElse:
-          hasElse = true
-          branch[0]
-        else:
-          raiseAssert "Malformed `case` statement in endsInNoReturn"
-    # Can only guarantee a noreturn if there is an else or it's exhaustive
-    result = hasElse
-  of nkTryStmt:
-    checkBranch(it[0])
-    for i in 1 ..< it.len:
-      let branch = it[i]
-      checkBranch(branch[^1])
-    # none of the branches returned
-    result = true
-  else:
-    result = it.kind in nkLastBlockStmts or
-      it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
+proc endsInNoReturn(n: PNode): bool
 
 proc commonType*(c: PContext; x: PType, y: PNode): PType =
   # ignore exception raising branches in case/if expressions
@@ -313,6 +254,8 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
     result.owner = getCurrOwner(c)
   else:
     result = newSym(kind, considerQuotedIdent(c, n), c.idgen, getCurrOwner(c), n.info)
+    if find(result.name.s, '`') >= 0:
+      result.flags.incl sfWasGenSym
   #if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
   #  incl(result.flags, sfGlobal)
   when defined(nimsuggest):
@@ -322,7 +265,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
                  allowed: TSymFlags): PSym
   # identifier with visibility
 proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
-                        allowed: TSymFlags): PSym
+                        allowed: TSymFlags, fromTopLevel = false): PSym
 
 proc typeAllowedCheck(c: PContext; info: TLineInfo; typ: PType; kind: TSymKind;
                       flags: TTypeAllowedFlags = {}) =
@@ -554,7 +497,6 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
 
 const
   errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters"
-  errFloatToString = "cannot convert '$1' to '$2'"
 
 proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
                   flags: TExprFlags = {}; expectedType: PType = nil): PNode =
@@ -708,7 +650,8 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault:
 
 proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode =
   let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes)
-  if aTypSkip.kind == tyObject:
+  case aTypSkip.kind
+  of tyObject:
     let child = defaultFieldsForTheUninitialized(c, aTypSkip.n, checkDefault)
     if child.len > 0:
       var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp))
@@ -717,7 +660,7 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): P
       result = semExpr(c, asgnExpr)
     else:
       result = nil
-  elif aTypSkip.kind == tyArray:
+  of tyArray:
     let child = defaultNodeField(c, a, aTypSkip[1], checkDefault)
 
     if child != nil:
@@ -730,7 +673,7 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): P
       result.typ = aTyp
     else:
       result = nil
-  elif aTypSkip.kind == tyTuple:
+  of tyTuple:
     var hasDefault = false
     if aTypSkip.n != nil:
       let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault, checkDefault)
@@ -743,6 +686,11 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): P
         result = nil
     else:
       result = nil
+  of tyRange:
+    if c.graph.config.isDefined("nimPreviewRangeDefault"):
+      result = firstRange(c.config, aTypSkip)
+    else:
+      result = nil
   else:
     result = nil
 
@@ -779,6 +727,7 @@ proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PCo
   result.semOverloadedCall = semOverloadedCall
   result.semInferredLambda = semInferredLambda
   result.semGenerateInstance = generateInstance
+  result.instantiateOnlyProcType = instantiateOnlyProcType
   result.semTypeNode = semTypeNode
   result.instTypeBoundOp = sigmatch.instTypeBoundOp
   result.hasUnresolvedArgs = hasUnresolvedArgs
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index a136cf4fe..13f2273a9 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -246,7 +246,16 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
       candidates.add(getProcHeader(c.config, err.sym, prefer))
     candidates.addDeclaredLocMaybe(c.config, err.sym)
     candidates.add("\n")
-    let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil
+    const genericParamMismatches = {kGenericParamTypeMismatch, kExtraGenericParam, kMissingGenericParam}
+    let isGenericMismatch = err.firstMismatch.kind in genericParamMismatches
+    var argList = n
+    if isGenericMismatch and n[0].kind == nkBracketExpr:
+      argList = n[0]
+    let nArg =
+      if err.firstMismatch.arg < argList.len:
+        argList[err.firstMismatch.arg]
+      else:
+        nil
     let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: ""
     if n.len > 1:
       if verboseTypeMismatch notin c.config.legacyFeatures:
@@ -269,6 +278,12 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
         of kMissingParam:
           candidates.add("  missing parameter: " & nameParam)
           candidates.add "\n"
+        of kExtraGenericParam:
+          candidates.add("  extra generic param given")
+          candidates.add "\n"
+        of kMissingGenericParam:
+          candidates.add("  missing generic parameter: " & nameParam)
+          candidates.add "\n"
         of kVarNeeded:
           doAssert nArg != nil
           doAssert err.firstMismatch.formal != nil
@@ -278,6 +293,8 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
           candidates.add "\n"
         of kTypeMismatch:
           doAssert nArg != nil
+          if nArg.kind in nkSymChoices:
+            candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
           let wanted = err.firstMismatch.formal.typ
           doAssert err.firstMismatch.formal != nil
           doAssert wanted != nil
@@ -292,9 +309,39 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
             candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
             effectProblem(wanted, got, candidates, c)
             candidates.add "\n"
+        of kGenericParamTypeMismatch:
+          let pos = err.firstMismatch.arg
+          doAssert n[0].kind == nkBracketExpr and pos < n[0].len
+          let arg = n[0][pos]
+          doAssert arg != nil
+          var wanted = err.firstMismatch.formal.typ
+          if wanted.kind == tyGenericParam and wanted.genericParamHasConstraints:
+            wanted = wanted.genericConstraint
+          let got = arg.typ.skipTypes({tyTypeDesc})
+          doAssert err.firstMismatch.formal != nil
+          doAssert wanted != nil
+          doAssert got != nil
+          candidates.add "  generic parameter mismatch, expected "
+          candidates.addTypeDeclVerboseMaybe(c.config, wanted)
+          candidates.add " but got '"
+          candidates.add renderTree(arg)
+          candidates.add "' of type: "
+          candidates.addTypeDeclVerboseMaybe(c.config, got)
+          if nArg.kind in nkSymChoices:
+            candidates.add "\n"
+            candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
+          if got != nil and got.kind == tyProc and wanted.kind == tyProc:
+            # These are proc mismatches so,
+            # add the extra explict detail of the mismatch
+            candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
+          if got != nil:
+            effectProblem(wanted, got, candidates, c)
+          candidates.add "\n"
         of kUnknown: discard "do not break 'nim check'"
       else:
         candidates.add("  first type mismatch at position: " & $err.firstMismatch.arg)
+        if err.firstMismatch.kind in genericParamMismatches:
+          candidates.add(" in generic parameters")
         # candidates.add "\n  reason: " & $err.firstMismatch.kind # for debugging
         case err.firstMismatch.kind
         of kUnknownNamedParam:
@@ -306,9 +353,16 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
         of kPositionalAlreadyGiven: candidates.add("\n  positional param was already given as named param")
         of kExtraArg: candidates.add("\n  extra argument given")
         of kMissingParam: candidates.add("\n  missing parameter: " & nameParam)
-        of kTypeMismatch, kVarNeeded:
+        of kExtraGenericParam:
+          candidates.add("\n  extra generic param given")
+        of kMissingGenericParam:
+          candidates.add("\n  missing generic parameter: " & nameParam)
+        of kTypeMismatch, kGenericParamTypeMismatch, kVarNeeded:
           doAssert nArg != nil
-          let wanted = err.firstMismatch.formal.typ
+          var wanted = err.firstMismatch.formal.typ
+          if isGenericMismatch and wanted.kind == tyGenericParam and
+              wanted.genericParamHasConstraints:
+            wanted = wanted.genericConstraint
           doAssert err.firstMismatch.formal != nil
           candidates.add("\n  required type for " & nameParam &  ": ")
           candidates.addTypeDeclVerboseMaybe(c.config, wanted)
@@ -319,8 +373,12 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
           else:
             candidates.add renderTree(nArg)
             candidates.add "' is of type: "
-            let got = nArg.typ
+            var got = nArg.typ
+            if isGenericMismatch: got = got.skipTypes({tyTypeDesc})
             candidates.addTypeDeclVerboseMaybe(c.config, got)
+            if nArg.kind in nkSymChoices:
+              candidates.add "\n"
+              candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
             doAssert wanted != nil
             if got != nil:
               if got.kind == tyProc and wanted.kind == tyProc:
@@ -331,8 +389,8 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
 
         of kUnknown: discard "do not break 'nim check'"
         candidates.add "\n"
-      if err.firstMismatch.arg == 1 and nArg.kind == nkTupleConstr and
-          n.kind == nkCommand:
+      if err.firstMismatch.arg == 1 and nArg != nil and
+          nArg.kind == nkTupleConstr and n.kind == nkCommand:
         maybeWrongSpace = true
     for diag in err.diagnostics:
       candidates.add(diag & "\n")
@@ -409,23 +467,6 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
       result.add("\n" & errExpectedPosition & "\n" & candidates)
     localError(c.config, n.info, result)
 
-proc bracketNotFoundError(c: PContext; n: PNode) =
-  var errors: CandidateErrors = @[]
-  var o: TOverloadIter = default(TOverloadIter)
-  let headSymbol = n[0]
-  var symx = initOverloadIter(o, c, headSymbol)
-  while symx != nil:
-    if symx.kind in routineKinds:
-      errors.add(CandidateError(sym: symx,
-                                firstMismatch: MismatchInfo(),
-                                diagnostics: @[],
-                                enabled: false))
-    symx = nextOverloadIter(o, c, headSymbol)
-  if errors.len == 0:
-    localError(c.config, n.info, "could not resolve: " & $n)
-  else:
-    notFoundError(c, n, errors)
-
 proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string =
   result = ""
   if c.compilesContextId > 0:
@@ -518,7 +559,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
     if overloadsState == csEmpty and result.state == csEmpty:
       if efNoUndeclared notin flags: # for tests/pragmas/tcustom_pragma.nim
         result.state = csNoMatch
-        if efNoDiagnostics in flags:
+        if c.inGenericContext > 0 and nfExprCall in n.flags:
+          # untyped expression calls end up here, see #24099
           return
         # xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident)
         localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f))
@@ -554,6 +596,39 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
         getProcHeader(c.config, alt.calleeSym),
         args])
 
+proc bracketNotFoundError(c: PContext; n: PNode; flags: TExprFlags) =
+  var errors: CandidateErrors = @[]
+  let headSymbol = n[0]
+  block:
+    # we build a closed symchoice of all `[]` overloads for their errors,
+    # except add a custom error for the magics which always match
+    var choice = newNodeIT(nkClosedSymChoice, headSymbol.info, newTypeS(tyNone, c))
+    var o: TOverloadIter = default(TOverloadIter)
+    var symx = initOverloadIter(o, c, headSymbol)
+    while symx != nil:
+      if symx.kind in routineKinds:
+        if symx.magic in {mArrGet, mArrPut}:
+          errors.add(CandidateError(sym: symx,
+                                    firstMismatch: MismatchInfo(),
+                                    diagnostics: @[],
+                                    enabled: false))
+        else:
+          choice.add newSymNode(symx, headSymbol.info)
+      symx = nextOverloadIter(o, c, headSymbol)
+    n[0] = choice
+  # copied from semOverloadedCallAnalyzeEffects, might be overkill:
+  const baseFilter = {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}
+  let filter =
+    if flags*{efInTypeof, efWantIterator, efWantIterable} != {}:
+      baseFilter + {skIterator}
+    else: baseFilter
+  # this will add the errors:
+  var r = resolveOverloads(c, n, n, filter, flags, errors, true)
+  if errors.len == 0:
+    localError(c.config, n.info, "could not resolve: " & $n)
+  else:
+    notFoundError(c, n, errors)
+
 proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
   let a = if a.kind == nkHiddenDeref: a[0] else: a
   if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
@@ -595,7 +670,7 @@ proc inferWithMetatype(c: PContext, formal: PType,
     result = copyTree(arg)
     result.typ = formal
 
-proc updateDefaultParams(call: PNode) =
+proc updateDefaultParams(c: PContext, call: PNode) =
   # In generic procs, the default parameter may be unique for each
   # instantiation (see tlateboundgenericparams).
   # After a call is resolved, we need to re-assign any default value
@@ -605,8 +680,18 @@ proc updateDefaultParams(call: PNode) =
   let calleeParams = call[0].sym.typ.n
   for i in 1..<call.len:
     if nfDefaultParam in call[i].flags:
-      let def = calleeParams[i].sym.ast
+      let formal = calleeParams[i].sym
+      let def = formal.ast
       if nfDefaultRefsParam in def.flags: call.flags.incl nfDefaultRefsParam
+      # mirrored with sigmatch:
+      if def.kind == nkEmpty:
+        # The default param value is set to empty in `instantiateProcType`
+        # when the type of the default expression doesn't match the type
+        # of the instantiated proc param:
+        pushInfoContext(c.config, call.info, call[0].sym.detailedInfo)
+        typeMismatch(c.config, def.info, formal.typ, def.typ, formal.ast)
+        popInfoContext(c.config)
+        def.typ = errorType(c)
       call[i] = def
 
 proc getCallLineInfo(n: PNode): TLineInfo =
@@ -686,12 +771,12 @@ proc semResolvedCall(c: PContext, x: var TCandidate,
   markUsed(c, info, finalCallee)
   onUse(info, finalCallee)
   assert finalCallee.ast != nil
-  if x.hasFauxMatch:
+  if x.matchedErrorType:
     result = x.call
     result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
-    if containsGenericType(result.typ) or x.fauxMatch == tyUnknown:
-      result.typ = newTypeS(x.fauxMatch, c)
-      if result.typ.kind == tyError: incl result.typ.flags, tfCheckedForDestructor
+    if containsGenericType(result.typ):
+      result.typ = newTypeS(tyError, c)
+      incl result.typ.flags, tfCheckedForDestructor
     return
   let gp = finalCallee.ast[genericParamsPos]
   if gp.isGenericParams:
@@ -725,8 +810,9 @@ proc semResolvedCall(c: PContext, x: var TCandidate,
   result = x.call
   instGenericConvertersSons(c, result, x)
   result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
-  result.typ = finalCallee.typ.returnType
-  updateDefaultParams(result)
+  if finalCallee.magic notin {mArrGet, mArrPut}:
+    result.typ = finalCallee.typ.returnType
+  updateDefaultParams(c, result)
 
 proc canDeref(n: PNode): bool {.inline.} =
   result = n.len >= 2 and (let t = n[1].typ;
@@ -751,7 +837,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
               candidates)
     result = semResolvedCall(c, r, n, flags, expectedType)
   else:
-    if efDetermineType in flags and c.inGenericContext > 0 and c.matchedConcept == nil:
+    if c.inGenericContext > 0 and c.matchedConcept == nil:
       result = semGenericStmt(c, n)
       result.typ = makeTypeFromExpr(c, result.copyTree)
     elif efExplain notin flags:
@@ -769,21 +855,16 @@ proc explicitGenericInstError(c: PContext; n: PNode): PNode =
   result = n
 
 proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
+  if s.kind in {skTemplate, skMacro}:
+    internalError c.config, n.info, "cannot get explicitly instantiated symbol of " &
+      (if s.kind == skTemplate: "template" else: "macro")
   # binding has to stay 'nil' for this to work!
   var m = newCandidate(c, s, nil)
-
-  for i in 1..<n.len:
-    let formal = s.ast[genericParamsPos][i-1].typ
-    var arg = n[i].typ
-    # try transforming the argument into a static one before feeding it into
-    # typeRel
-    if formal.kind == tyStatic and arg.kind != tyStatic:
-      let evaluated = c.semTryConstExpr(c, n[i], n[i].typ)
-      if evaluated != nil:
-        arg = newTypeS(tyStatic, c, son = evaluated.typ)
-        arg.n = evaluated
-    let tm = typeRel(m, formal, arg)
-    if tm in {isNone, isConvertible}: return nil
+  matchGenericParams(m, n, s)
+  if m.state != csMatch:
+    # state is csMatch only if *all* generic params were matched,
+    # including implicit parameters
+    return nil
   var newInst = generateInstance(c, s, m.bindings, n.info)
   newInst.typ.flags.excl tfUnresolved
   let info = getCallLineInfo(n)
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 12930feca..ca35ddc53 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -73,9 +73,9 @@ type
     efNoUndeclared, efIsDotCall, efCannotBeDotCall,
       # Use this if undeclared identifiers should not raise an error during
       # overload resolution.
-    efNoDiagnostics,
     efTypeAllowed # typeAllowed will be called after
     efWantNoDefaults
+    efIgnoreDefaults # var statements without initialization
     efAllowSymChoice # symchoice node should not be resolved
 
   TExprFlags* = set[TExprFlag]
@@ -139,6 +139,9 @@ type
     semInferredLambda*: proc(c: PContext, pt: Table[ItemId, PType], n: PNode): PNode
     semGenerateInstance*: proc (c: PContext, fn: PSym, pt: Table[ItemId, PType],
                                 info: TLineInfo): PSym
+    instantiateOnlyProcType*: proc (c: PContext, pt: TypeMapping,
+                                    prc: PSym, info: TLineInfo): PType
+      # used by sigmatch for explicit generic instantiations
     includedFiles*: IntSet    # used to detect recursive include files
     pureEnumFields*: TStrTable   # pure enum fields that can be used unambiguously
     userPragmas*: TStrTable
@@ -168,6 +171,7 @@ type
     inUncheckedAssignSection*: int
     importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id])
     skipTypes*: seq[PNode] # used to skip types between passes in type section. So far only used for inheritance, sets and generic bodies.
+    inTypeofContext*: int
   TBorrowState* = enum
     bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch
 
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 17a3082ff..2885142a7 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -138,7 +138,71 @@ proc resolveSymChoice(c: PContext, n: var PNode, flags: TExprFlags = {}, expecte
       # to mirror behavior before overloadable enums
       n = n[0]
 
+proc semOpenSym(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType,
+                warnDisabled = false): PNode =
+  ## sem the child of an `nkOpenSym` node, that is, captured symbols that can be
+  ## replaced by newly injected symbols in generics. `s` must be the captured
+  ## symbol if the original node is an `nkSym` node; and `nil` if it is an
+  ## `nkOpenSymChoice`, in which case only non-overloadable injected symbols
+  ## will be considered.
+  let isSym = n.kind == nkSym
+  let ident = n.getPIdent
+  assert ident != nil
+  let id = newIdentNode(ident, n.info)
+  c.isAmbiguous = false
+  let s2 = qualifiedLookUp(c, id, {})
+  # for `nkSym`, the first found symbol being different and unambiguous is
+  # enough to replace the original
+  # for `nkOpenSymChoice`, the first found symbol must be non-overloadable,
+  # since otherwise we have to use regular `nkOpenSymChoice` functionality
+  # but of the overloadable sym kinds, semExpr does not handle skModule, skMacro, skTemplate
+  # as overloaded in the case where `nkIdent` finds them first
+  if s2 != nil and not c.isAmbiguous and
+      ((isSym and s2 != n.sym) or
+        (not isSym and s2.kind notin OverloadableSyms-{skModule, skMacro, skTemplate})):
+    # only consider symbols defined under current proc:
+    var o = s2.owner
+    while o != nil:
+      if o == c.p.owner:
+        if not warnDisabled:
+          result = semExpr(c, id, flags, expectedType)
+          return
+        else:
+          var msg =
+            "a new symbol '" & ident.s & "' has been injected during " &
+            # msgContext should show what is being instantiated:
+            "template or generic instantiation, however "
+          if isSym:
+            msg.add(
+              getSymRepr(c.config, n.sym) & " captured at " &
+              "the proc declaration will be used instead; " &
+              "either enable --experimental:openSym to use the injected symbol, " &
+              "or `bind` this captured symbol explicitly")
+          else:
+            msg.add(
+              "overloads of " & ident.s & " will be used instead; " &
+              "either enable --experimental:openSym to use the injected symbol, " &
+              "or `bind` this symbol explicitly")
+          message(c.config, n.info, warnIgnoredSymbolInjection, msg)
+          break
+      o = o.owner
+  # nothing found
+  n.flags.excl nfDisabledOpenSym
+  if not warnDisabled and isSym:
+    result = semExpr(c, n, flags, expectedType)
+  else:
+    result = nil
+    if not isSym:
+      # set symchoice node type back to None
+      n.typ = newTypeS(tyNone, c)
+
 proc semSymChoice(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
+  if n.kind == nkOpenSymChoice:
+    result = semOpenSym(c, n, flags, expectedType,
+      warnDisabled = nfDisabledOpenSym in n.flags and
+        genericsOpenSym notin c.features)
+    if result != nil:
+      return
   result = n
   resolveSymChoice(c, result, flags, expectedType)
   if isSymChoice(result) and result.len == 1:
@@ -218,14 +282,16 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus =
     result = checkConversionBetweenObjects(d.skipTypes(abstractInst), s.skipTypes(abstractInst), pointers)
   elif (targetBaseTyp.kind in IntegralTypes) and
       (srcBaseTyp.kind in IntegralTypes):
-    if targetTyp.kind == tyEnum and srcBaseTyp.kind == tyEnum:
+    if targetTyp.kind == tyEnum and srcBaseTyp.kind == tyEnum and
+        not sameType(targetTyp, srcBaseTyp):
       message(c.config, src.info, warnSuspiciousEnumConv, "suspicious code: enum to enum conversion")
     # `elif` would be incorrect here
     if targetTyp.kind == tyBool:
       discard "convOk"
     elif targetTyp.isOrdinalType:
       if src.kind in nkCharLit..nkUInt64Lit and
-          src.getInt notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp):
+          src.getInt notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp) and
+          targetTyp.kind notin {tyUInt..tyUInt64}:
         result = convNotInRange
       elif src.kind in nkFloatLit..nkFloat64Lit and
           (classify(src.floatVal) in {fcNan, fcNegInf, fcInf} or
@@ -554,15 +620,14 @@ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
       n[1] = makeTypeSymNode(c, lhsType, n[1].info)
       lhsType = n[1].typ
   else:
-    if lhsType.base.kind == tyNone or
-        (c.inGenericContext > 0 and lhsType.base.containsGenericType):
+    if c.inGenericContext > 0 and lhsType.base.containsUnresolvedType:
       # BUGFIX: don't evaluate this too early: ``T is void``
       return
 
   result = isOpImpl(c, n, flags)
 
 proc semOpAux(c: PContext, n: PNode) =
-  const flags = {efDetermineType}
+  const flags = {efDetermineType, efAllowSymChoice}
   for i in 1..<n.len:
     var a = n[i]
     if a.kind == nkExprEqExpr and a.len == 2:
@@ -587,7 +652,14 @@ proc overloadedCallOpr(c: PContext, n: PNode): PNode =
 
 proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
   case n.kind
-  of nkCurly, nkBracket:
+  of nkCurly:
+    for i in 0..<n.len:
+      if n[i].kind == nkRange:
+        changeType(c, n[i][0], elemType(newType), check)
+        changeType(c, n[i][1], elemType(newType), check)
+      else:
+        changeType(c, n[i], elemType(newType), check)
+  of nkBracket:
     for i in 0..<n.len:
       changeType(c, n[i], elemType(newType), check)
   of nkPar, nkTupleConstr:
@@ -624,10 +696,16 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
       let value = n.intVal
       if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
         localError(c.config, n.info, "cannot convert " & $value &
-                                         " to " & typeToString(newType))
+                                         " to " & typeNameAndDesc(newType))
   of nkFloatLit..nkFloat64Lit:
     if check and not floatRangeCheck(n.floatVal, newType):
-      localError(c.config, n.info, errFloatToString % [$n.floatVal, typeToString(newType)])
+      localError(c.config, n.info, errFloatToString % [$n.floatVal, typeNameAndDesc(newType)])
+  of nkSym:
+    if check and n.sym.kind == skEnumField and not sameTypeOrNil(n.sym.typ, newType):
+      let value = n.sym.position
+      if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
+        localError(c.config, n.info, "cannot convert '" & n.sym.name.s &
+                                         "' to '" & typeNameAndDesc(newType) & "'")
   else: discard
   n.typ = newType
 
@@ -644,29 +722,41 @@ proc arrayConstrType(c: PContext, n: PNode): PType =
 
 proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
   result = newNodeI(nkBracket, n.info)
-  result.typ = newTypeS(tyArray, c)
+  # nkBracket nodes can also be produced by the VM as seq constant nodes
+  # in which case, we cannot produce a new array type for the node,
+  # as this might lose type info even when the node has array type
+  let constructType = n.typ.isNil
   var expectedElementType, expectedIndexType: PType = nil
-  if expectedType != nil:
-    let expected = expectedType.skipTypes(abstractRange-{tyDistinct})
-    case expected.kind
+  var expectedBase: PType = nil
+  if constructType:
+    result.typ = newTypeS(tyArray, c)
+    rawAddSon(result.typ, nil)     # index type
+    if expectedType != nil:
+      expectedBase = expectedType.skipTypes(abstractRange-{tyDistinct})
+  else:
+    result.typ = n.typ
+    expectedBase = n.typ.skipTypes(abstractRange) # include tyDistinct this time
+  if expectedBase != nil:
+    case expectedBase.kind
     of tyArray:
-      expectedIndexType = expected[0]
-      expectedElementType = expected[1]
-    of tyOpenArray:
-      expectedElementType = expected[0]
+      expectedIndexType = expectedBase[0]
+      expectedElementType = expectedBase[1]
+    of tyOpenArray, tySequence:
+      # typed bracket expressions can also have seq type
+      expectedElementType = expectedBase[0]
     else: discard
-  rawAddSon(result.typ, nil)     # index type
   var
     firstIndex, lastIndex: Int128 = Zero
     indexType = getSysType(c.graph, n.info, tyInt)
     lastValidIndex = lastOrd(c.config, indexType)
   if n.len == 0:
-    rawAddSon(result.typ,
-      if expectedElementType != nil and
-          typeAllowed(expectedElementType, skLet, c) == nil:
-        expectedElementType
-      else:
-        newTypeS(tyEmpty, c)) # needs an empty basetype!
+    if constructType:
+      rawAddSon(result.typ,
+        if expectedElementType != nil and
+            typeAllowed(expectedElementType, skLet, c) == nil:
+          expectedElementType
+        else:
+          newTypeS(tyEmpty, c)) # needs an empty basetype!
     lastIndex = toInt128(-1)
   else:
     var x = n[0]
@@ -683,9 +773,13 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
         x = x[1]
 
     let yy = semExprWithType(c, x, {efTypeAllowed}, expectedElementType)
-    var typ = yy.typ
-    if expectedElementType == nil:
-      expectedElementType = typ
+    var typ: PType
+    if constructType:
+      typ = yy.typ
+      if expectedElementType == nil:
+        expectedElementType = typ
+    else:
+      typ = expectedElementType
     result.add yy
     #var typ = skipTypes(result[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal})
     for i in 1..<n.len:
@@ -705,15 +799,20 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
 
       let xx = semExprWithType(c, x, {efTypeAllowed}, expectedElementType)
       result.add xx
-      typ = commonType(c, typ, xx.typ)
+      if constructType:
+        typ = commonType(c, typ, xx.typ)
       #n[i] = semExprWithType(c, x, {})
       #result.add fitNode(c, typ, n[i])
       inc(lastIndex)
-    addSonSkipIntLit(result.typ, typ, c.idgen)
+    if constructType:
+      addSonSkipIntLit(result.typ, typ, c.idgen)
     for i in 0..<result.len:
       result[i] = fitNode(c, typ, result[i], result[i].info)
-  result.typ.setIndexType makeRangeType(c, toInt64(firstIndex), toInt64(lastIndex), n.info,
-                                     indexType)
+  if constructType:
+    result.typ.setIndexType(
+      makeRangeType(c,
+        toInt64(firstIndex), toInt64(lastIndex),
+        n.info, indexType))
 
 proc fixAbstractType(c: PContext, n: PNode) =
   for i in 1..<n.len:
@@ -738,7 +837,7 @@ proc isUnresolvedSym(s: PSym): bool =
   result = s.kind == skGenericParam
   if not result and s.typ != nil:
     result = tfInferrableStatic in s.typ.flags or
-        (s.kind == skParam and s.typ.isMetaType) or
+        (s.kind == skParam and (s.typ.isMetaType or sfTemplateParam in s.flags)) or
         (s.kind == skType and
         s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
 
@@ -812,10 +911,13 @@ proc analyseIfAddressTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
 
 proc analyseIfAddressTakenInCall(c: PContext, n: PNode, isConverter = false) =
   checkMinSonsLen(n, 1, c.config)
+  if n[0].typ == nil:
+    # n[0] might be erroring node in nimsuggest
+    return
   const
     FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl,
       mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap,
-      mAppendSeqElem, mNewSeq, mReset, mShallowCopy, mDeepCopy, mMove,
+      mAppendSeqElem, mNewSeq, mShallowCopy, mDeepCopy, mMove,
       mWasMoved}
 
   template checkIfConverterCalled(c: PContext, n: PNode) =
@@ -914,7 +1016,8 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
 
     if callee.magic notin ctfeWhitelist: return
 
-    if callee.kind notin {skProc, skFunc, skConverter, skConst} or callee.isGenericRoutine:
+    if callee.kind notin {skProc, skFunc, skConverter, skConst} or
+        callee.isGenericRoutineStrict:
       return
 
     if n.typ != nil and typeAllowed(n.typ, skConst, c) != nil: return
@@ -971,7 +1074,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
 
   if result != nil:
     if result[0].kind != nkSym:
-      if not (efDetermineType in flags and c.inGenericContext > 0):
+      if not (c.inGenericContext > 0): # see generic context check in semOverloadedCall
         internalError(c.config, "semOverloadedCallAnalyseEffects")
       return
     let callee = result[0].sym
@@ -995,14 +1098,6 @@ proc resolveIndirectCall(c: PContext; n, nOrig: PNode;
   result = initCandidate(c, t)
   matches(c, n, nOrig, result)
 
-proc bracketedMacro(n: PNode): PSym =
-  if n.len >= 1 and n[0].kind == nkSym:
-    result = n[0].sym
-    if result.kind notin {skMacro, skTemplate}:
-      result = nil
-  else:
-    result = nil
-
 proc finishOperand(c: PContext, a: PNode): PNode =
   if a.typ.isNil:
     result = c.semOperand(c, a, {efDetermineType})
@@ -1021,10 +1116,14 @@ proc finishOperand(c: PContext, a: PNode): PNode =
     localError(c.config, a.info, err)
   considerGenSyms(c, result)
 
-proc semFinishOperands(c: PContext; n: PNode) =
+proc semFinishOperands(c: PContext; n: PNode; isBracketExpr = false) =
   # this needs to be called to ensure that after overloading resolution every
-  # argument has been sem'checked:
-  for i in 1..<n.len:
+  # argument has been sem'checked
+
+  # skip the first argument for operands of `[]` since it may be an unresolved
+  # generic proc, which is handled in semMagic
+  let start = 1 + ord(isBracketExpr)
+  for i in start..<n.len:
     n[i] = finishOperand(c, n[i])
 
 proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
@@ -1047,10 +1146,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedTy
   of skMacro: result = semMacroExpr(c, result, orig, callee, flags, expectedType)
   of skTemplate: result = semTemplateExpr(c, result, callee, flags, expectedType)
   else:
-    if callee.magic notin {mArrGet, mArrPut, mNBindSym}:
-      # calls to `[]` can be explicit generic instantiations,
-      # don't sem every operand now, leave it to semmagic
-      semFinishOperands(c, result)
+    semFinishOperands(c, result, isBracketExpr = callee.magic in {mArrGet, mArrPut})
     activate(c, result)
     fixAbstractType(c, result)
     analyseIfAddressTakenInCall(c, result)
@@ -1061,7 +1157,8 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedTy
           not (result.typ.kind == tySequence and result.elementType.kind == tyEmpty):
         liftTypeBoundOps(c, result.typ, n.info)
     #result = patchResolvedTypeBoundOp(c, result)
-  if c.matchedConcept == nil:
+  if c.matchedConcept == nil and (c.inTypeofContext == 0 or callee.magic != mNone):
+    # don't fold calls in concepts and typeof
     result = evalAtCompileTime(c, result)
 
 proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
@@ -1078,6 +1175,11 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
       result.flags.incl nfExplicitCall
       for i in 1..<n.len: result.add n[i]
       return semExpr(c, result, flags, expectedType)
+    elif n0.typ.kind == tyFromExpr and c.inGenericContext > 0:
+      # don't make assumptions, entire expression needs to be tyFromExpr
+      result = semGenericStmt(c, n)
+      result.typ = makeTypeFromExpr(c, result.copyTree)
+      return
     else:
       n[0] = n0
   else:
@@ -1085,11 +1187,6 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
     let t = n[0].typ
     if t != nil and t.kind in {tyVar, tyLent}:
       n[0] = newDeref(n[0])
-    elif n[0].kind == nkBracketExpr:
-      let s = bracketedMacro(n[0])
-      if s != nil:
-        setGenericParams(c, n[0], s.ast[genericParamsPos])
-        return semDirectOp(c, n, flags, expectedType)
     elif isSymChoice(n[0]) and nfDotField notin n.flags:
       # overloaded generic procs e.g. newSeq[int] can end up here
       return semDirectOp(c, n, flags, expectedType)
@@ -1418,20 +1515,34 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
 
 proc tryReadingGenericParam(c: PContext, n: PNode, i: PIdent, t: PType): PNode =
   case t.kind
-  of tyTypeParamsHolders:
+  of tyGenericInst:
     result = readTypeParameter(c, t, i, n.info)
     if result == c.graph.emptyNode:
-      result = n
-      n.typ = makeTypeFromExpr(c, n.copyTree)
+      if c.inGenericContext > 0:
+        result = semGenericStmt(c, n)
+        result.typ = makeTypeFromExpr(c, result.copyTree)
+      else:
+        result = nil
   of tyUserTypeClasses:
     if t.isResolvedUserTypeClass:
       result = readTypeParameter(c, t, i, n.info)
+    elif c.inGenericContext > 0:
+      result = semGenericStmt(c, n)
+      result.typ = makeTypeFromExpr(c, copyTree(result))
     else:
-      n.typ = makeTypeFromExpr(c, copyTree(n))
-      result = n
-  of tyGenericParam, tyAnything:
-    n.typ = makeTypeFromExpr(c, copyTree(n))
-    result = n
+      result = nil
+  of tyGenericBody, tyCompositeTypeClass:
+    if c.inGenericContext > 0:
+      result = readTypeParameter(c, t, i, n.info)
+      if result != nil:
+        # generic parameter exists, stop here but delay until instantiation
+        result = semGenericStmt(c, n)
+        result.typ = makeTypeFromExpr(c, copyTree(result))
+    else:
+      result = nil
+  elif c.inGenericContext > 0 and t.containsUnresolvedType:
+    result = semGenericStmt(c, n)
+    result.typ = makeTypeFromExpr(c, copyTree(result))
   else:
     result = nil
 
@@ -1595,18 +1706,20 @@ proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode =
   result.add(newIdentNode(ident, n.info))
   for s in n: result.add s
 
-proc semDeref(c: PContext, n: PNode): PNode =
+proc semDeref(c: PContext, n: PNode, flags: TExprFlags): PNode =
   checkSonsLen(n, 1, c.config)
   n[0] = semExprWithType(c, n[0])
   let a = getConstExpr(c.module, n[0], c.idgen, c.graph)
   if a != nil:
-    if a.kind == nkNilLit:
+    if a.kind == nkNilLit and efInTypeof notin flags:
       localError(c.config, n.info, "nil dereference is not allowed")
     n[0] = a
   result = n
   var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink, tyOwned})
   case t.kind
   of tyRef, tyPtr: n.typ = t.elementType
+  of tyMetaTypes, tyFromExpr:
+    n.typ = makeTypeFromExpr(c, n.copyTree)
   else: result = nil
   #GlobalError(n[0].info, errCircumNeedsPointer)
 
@@ -1629,16 +1742,17 @@ proc maybeInstantiateGeneric(c: PContext, n: PNode, s: PSym): PNode =
     result = explicitGenericInstantiation(c, n, s)
     if result == n:
       n[0] = copyTree(result[0])
-    else:
-      n[0] = result
 
 proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
   ## returns nil if not a built-in subscript operator; also called for the
   ## checking of assignments
   result = nil
   if n.len == 1:
-    let x = semDeref(c, n)
+    let x = semDeref(c, n, flags)
     if x == nil: return nil
+    if x.typ.kind == tyFromExpr:
+      # depends on generic type
+      return x
     result = newNodeIT(nkDerefExpr, x.info, x.typ)
     result.add(x[0])
     return
@@ -1703,7 +1817,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
       result = nil
   else:
     let s = if n[0].kind == nkSym: n[0].sym
-            elif n[0].kind in nkSymChoices: n[0][0].sym
+            elif n[0].kind in nkSymChoices + {nkOpenSym}: n[0][0].sym
             else: nil
     if s != nil:
       case s.kind
@@ -1784,6 +1898,8 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode =
     else:
       localError(c.config, n.info, errExprHasNoAddress)
   result = newNodeIT(nkHiddenAddr, n.info, if n.typ.kind in {tyVar, tyLent}: n.typ else: makePtrType(c, n.typ))
+  if n.typ.kind in {tyVar, tyLent}:
+    n.typ = n.typ.elementType
   result.add(n)
 
 proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
@@ -1910,7 +2026,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
       result = buildOverloadedSubscripts(n[0], getIdent(c.cache, "[]="))
       result.add(n[1])
       if mode == noOverloadedSubscript:
-        bracketNotFoundError(c, result)
+        bracketNotFoundError(c, result, {})
         return errorNode(c, n)
       else:
         result = semExprNoType(c, result)
@@ -1957,7 +2073,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
         if rhsTyp.kind in tyUserTypeClasses and rhsTyp.isResolvedUserTypeClass:
           rhsTyp = rhsTyp.last
         if lhs.sym.typ.kind == tyAnything:
-          rhsTyp = rhsTyp.skipIntLit(c.idgen)
+          rhsTyp = rhsTyp.skipTypes({tySink}).skipIntLit(c.idgen)
         if cmpTypes(c, lhs.typ, rhsTyp) in {isGeneric, isEqual}:
           internalAssert c.config, c.p.resultSym != nil
           # Make sure the type is valid for the result variable
@@ -2139,6 +2255,8 @@ proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
     result = n.sym
   of nkOpenSymChoice, nkClosedSymChoice:
     result = n[0].sym
+  of nkOpenSym:
+    result = lookUpForDeclared(c, n[0], onlyCurrentScope)
   else:
     localError(c.config, n.info, "identifier expected, but got: " & renderTree(n))
     result = nil
@@ -2426,7 +2544,7 @@ proc instantiateCreateFlowVarCall(c: PContext; t: PType;
   # codegen would fail:
   if sfCompilerProc in result.flags:
     result.flags.excl {sfCompilerProc, sfExportc, sfImportc}
-    result.loc.r = ""
+    result.loc.snippet = ""
 
 proc setMs(n: PNode, s: PSym): PNode =
   result = n
@@ -2591,13 +2709,16 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
   var typ = commonTypeBegin
   if n.len in 1..2 and n[0].kind == nkElifBranch and (
       n.len == 1 or n[1].kind == nkElse):
-    let exprNode = n[0][0]
+    var exprNode = n[0][0]
+    if exprNode.kind == nkOpenSym:
+      exprNode = exprNode[0]
     if exprNode.kind == nkIdent:
       whenNimvm = lookUp(c, exprNode).magic == mNimvm
     elif exprNode.kind == nkSym:
       whenNimvm = exprNode.sym.magic == mNimvm
     if whenNimvm: n.flags.incl nfLL
 
+  var cannotResolve = false
   for i in 0..<n.len:
     var it = n[i]
     case it.kind
@@ -2608,6 +2729,19 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
           it[1] = semExpr(c, it[1], flags)
           typ = commonType(c, typ, it[1].typ)
         result = n # when nimvm is not elimited until codegen
+      elif c.inGenericContext > 0:
+        let e = semExprWithType(c, it[0])
+        if e.typ.kind == tyFromExpr:
+          it[0] = makeStaticExpr(c, e)
+          cannotResolve = true
+        else:
+          it[0] = forceBool(c, e)
+          let val = getConstExpr(c.module, it[0], c.idgen, c.graph)
+          if val == nil or val.kind != nkIntLit:
+            cannotResolve = true
+          elif not cannotResolve and val.intVal != 0 and result == nil:
+            setResult(it[1])
+            return # we're not in nimvm and we already have a result
       else:
         let e = forceBool(c, semConstExpr(c, it[0]))
         if e.kind != nkIntLit:
@@ -2619,13 +2753,21 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
           return # we're not in nimvm and we already have a result
     of nkElse, nkElseExpr:
       checkSonsLen(it, 1, c.config)
-      if result == nil or whenNimvm:
+      if cannotResolve:
+        discard
+      elif result == nil or whenNimvm:
         if semCheck:
           it[0] = semExpr(c, it[0], flags)
           typ = commonType(c, typ, it[0].typ)
+          if typ != nil and typ.kind != tyUntyped:
+            it[0] = fitNode(c, typ, it[0], it[0].info)
         if result == nil:
           result = it[0]
     else: illFormedAst(n, c.config)
+  if cannotResolve:
+    result = semGenericStmt(c, n)
+    result.typ = makeTypeFromExpr(c, result.copyTree)
+    return
   if result == nil:
     result = newNodeI(nkEmpty, n.info)
   if whenNimvm:
@@ -2779,7 +2921,7 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType
       n[i][1].typ = errorType(c)
 
     var f = newSymS(skField, n[i][0], c)
-    f.typ = skipIntLit(n[i][1].typ, c.idgen)
+    f.typ = skipIntLit(n[i][1].typ.skipTypes({tySink}), c.idgen)
     f.position = i
     rawAddSon(typ, f.typ)
     typ.n.add newSymNode(f)
@@ -2805,7 +2947,7 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedT
       # `const foo = [(1, {}), (2, {false})]`,
       # `const foo = if true: (0, nil) else: (1, new(int))`
       n[i] = fitNode(c, expectedElemType, n[i], n[i].info)
-    addSonSkipIntLit(typ, n[i].typ, c.idgen)
+    addSonSkipIntLit(typ, n[i].typ.skipTypes({tySink}), c.idgen)
   result.typ = typ
 
 include semobjconstr
@@ -2910,19 +3052,42 @@ proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
   else:
     result = tupexp
 
-proc shouldBeBracketExpr(n: PNode): bool =
-  result = false
+proc isExplicitGenericCall(c: PContext, n: PNode): bool =
+  ## checks if a call node `n` is a routine call with explicit generic params
+  ## 
+  ## the callee node needs to be either an nkBracketExpr or a call to a
+  ## symchoice of `[]` in which case it will be transformed into nkBracketExpr
+  ## 
+  ## the LHS of the bracket expr has to either be a symchoice or resolve to
+  ## a routine symbol
+  template checkCallee(n: PNode) =
+    # check subscript LHS, `n` must be mutable
+    if isSymChoice(n):
+      result = true
+    else:
+      let s = qualifiedLookUp(c, n, {})
+      if s != nil and s.kind in routineKinds:
+        result = true
+        n = semSymGenericInstantiation(c, n, s)
   assert n.kind in nkCallKinds
+  result = false
   let a = n[0]
-  if a.kind in nkCallKinds:
+  case a.kind
+  of nkBracketExpr:
+    checkCallee(a[0])
+  of nkCallKinds:
     let b = a[0]
     if b.kind in nkSymChoices:
-      for i in 0..<b.len:
-        if b[i].kind == nkSym and b[i].sym.magic == mArrGet:
-          let be = newNodeI(nkBracketExpr, n.info)
+      let name = b.getPIdent
+      if name != nil and name.s == "[]":
+        checkCallee(a[1])
+        if result:
+          # transform callee into normal bracket expr, only on success
+          let be = newNodeI(nkBracketExpr, a.info)
           for i in 1..<a.len: be.add(a[i])
           n[0] = be
-          return true
+  else:
+    result = false
 
 proc asBracketExpr(c: PContext; n: PNode): PNode =
   proc isGeneric(c: PContext; n: PNode): bool =
@@ -2943,6 +3108,18 @@ proc asBracketExpr(c: PContext; n: PNode): PNode =
           return result
   return nil
 
+proc isOpenArraySym(x: PNode): bool =
+  var x = x
+  while true:
+    case x.kind
+    of {nkAddr, nkHiddenAddr}:
+      x = x[0]
+    of {nkHiddenStdConv, nkHiddenDeref}:
+      x = x[1]
+    else:
+      break
+  result = x.kind == nkSym
+
 proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) =
   # This takes care of complicated signatures such as:
   # proc foo(a: int, b = a)
@@ -2960,10 +3137,17 @@ proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode)
   # duty is activated by returning a non-nil value. The caller is responsible
   # for replacing the input to the function with the returned non-nil value.
   # (which is the hoisted symbol)
-  if defExpr.kind == nkSym and defExpr.sym.kind == skParam and defExpr.sym.owner == call[0].sym:
+  if defExpr.kind == nkSym and defExpr.sym.kind == skParam and
+      (defExpr.sym.owner == call[0].sym or
+        # symbol was resolved before proc was instantiated:
+        (sfFromGeneric in call[0].sym.flags and
+          defExpr.sym.owner == call[0].sym.instantiatedFrom)):
     let paramPos = defExpr.sym.position + 1
 
-    if call[paramPos].skipAddr.kind != nkSym:
+    if call[paramPos].skipAddr.kind != nkSym and not (
+      skipTypes(call[paramPos].typ, abstractVar).kind in {tyOpenArray, tyVarargs} and
+      isOpenArraySym(call[paramPos])
+    ):
       let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), c.idgen,
                                  c.p.owner, letSection.info, c.p.owner.options)
       hoistedVarSym.typ = call[paramPos].typ
@@ -2989,7 +3173,7 @@ proc getNilType(c: PContext): PType =
     result.align = c.config.target.ptrSize.int16
     c.nilTypeCache = result
 
-proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode =
+proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym; flags: TExprFlags): PNode =
   var o: TOverloadIter = default(TOverloadIter)
   var i = 0
   var a = initOverloadIter(o, c, n)
@@ -3002,7 +3186,7 @@ proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode =
   if i <= 1:
     if sfGenSym notin s.flags:
       result = newSymNode(s, info)
-      markUsed(c, info, s)
+      markUsed(c, info, s, efInCall notin flags)
       onUse(info, s)
     else:
       result = n
@@ -3027,30 +3211,30 @@ proc resolveIdentToSym(c: PContext, n: PNode, resultNode: var PNode,
                        flags: TExprFlags, expectedType: PType): PSym =
   # result is nil on error or if a node that can't produce a sym is resolved
   let ident = considerQuotedIdent(c, n)
-  if expectedType != nil and (
-      let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
-      expected.kind == tyEnum):
-    let nameId = ident.id
-    for f in expected.n:
-      if f.kind == nkSym and f.sym.name.id == nameId:
-        return f.sym
   var filter = {low(TSymKind)..high(TSymKind)}
-  if efNoEvaluateGeneric in flags:
+  if efNoEvaluateGeneric in flags or expectedType != nil:
     # `a[...]` where `a` is a module or package is not possible
     filter.excl {skModule, skPackage}
-  let candidates = lookUpCandidates(c, ident, filter)
+  let includePureEnum = expectedType != nil and
+    expectedType.skipTypes(abstractRange-{tyDistinct}).kind == tyEnum
+  let candidates = lookUpCandidates(c, ident, filter,
+    includePureEnum = includePureEnum)
   if candidates.len == 0:
     result = errorUndeclaredIdentifierHint(c, ident, n.info)
   elif candidates.len == 1 or {efNoEvaluateGeneric, efInCall} * flags != {}:
     # unambiguous, or we don't care about ambiguity
     result = candidates[0]
   else:
-    # ambiguous symbols have 1 last chance as a symchoice,
-    # but type symbols cannot participate in symchoices
+    # ambiguous symbols have 1 last chance as a symchoice
     var choice = newNodeIT(nkClosedSymChoice, n.info, newTypeS(tyNone, c))
-    for c in candidates:
-      if c.kind notin {skType, skModule, skPackage}:
-        choice.add newSymNode(c, n.info)
+    for cand in candidates:
+      case cand.kind
+      of skModule, skPackage:
+        discard
+      of skType:
+        choice.add newSymNodeTypeDesc(cand, c.idgen, n.info)
+      else:
+        choice.add newSymNode(cand, n.info)
     if choice.len == 0:
       # we know candidates.len > 1, we just couldn't put any in a symchoice
       errorUseQualifier(c, n.info, candidates)
@@ -3126,46 +3310,35 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
       if optOwnedRefs in c.config.globalOptions:
         result.typ = makeVarType(c, result.typ, tyOwned)
     of skEnumField:
-      result = enumFieldSymChoice(c, n, s)
+      result = enumFieldSymChoice(c, n, s, flags)
     else:
       result = semSym(c, n, s, flags)
     if isSymChoice(result):
       result = semSymChoice(c, result, flags, expectedType)
   of nkClosedSymChoice, nkOpenSymChoice:
-    result = semSymChoice(c, result, flags, expectedType)
+    result = semSymChoice(c, n, flags, expectedType)
   of nkSym:
     let s = n.sym
-    if nfOpenSym in n.flags:
-      let id = newIdentNode(s.name, n.info)
-      c.isAmbiguous = false
-      let s2 = qualifiedLookUp(c, id, {})
-      if s2 != nil and s2 != s and not c.isAmbiguous:
-        # only consider symbols defined under current proc:
-        var o = s2.owner
-        while o != nil:
-          if o == c.p.owner:
-            if genericsOpenSym in c.features:
-              result = semExpr(c, id, flags, expectedType)
-              return
-            else:
-              message(c.config, n.info, warnGenericsIgnoredInjection,
-                "a new symbol '" & s.name.s & "' has been injected during " &
-                "instantiation of " & c.p.owner.name.s & ", " &
-                "however " & getSymRepr(c.config, s) & " captured at " &
-                "the proc declaration will be used instead; " &
-                "either enable --experimental:genericsOpenSym to use the " &
-                "injected symbol or `bind` this captured symbol explicitly")
-              break
-          o = o.owner
+    if nfDisabledOpenSym in n.flags:
+      let override = genericsOpenSym in c.features
+      let res = semOpenSym(c, n, flags, expectedType,
+        warnDisabled = not override)
+      if res != nil:
+        assert override
+        return res
     # because of the changed symbol binding, this does not mean that we
     # don't have to check the symbol for semantics here again!
     result = semSym(c, n, s, flags)
+  of nkOpenSym:
+    assert n.len == 1
+    let inner = n[0]
+    result = semOpenSym(c, inner, flags, expectedType)
   of nkEmpty, nkNone, nkCommentStmt, nkType:
     discard
   of nkNilLit:
     if result.typ == nil:
       result.typ = getNilType(c)
-      if expectedType != nil:
+      if expectedType != nil and expectedType.kind notin {tyUntyped, tyTyped}:
         var m = newCandidate(c, result.typ)
         if typeRel(m, expectedType, result.typ) >= isSubtype:
           result.typ = expectedType
@@ -3247,12 +3420,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
       of skType:
         # XXX think about this more (``set`` procs)
         let ambig = c.isAmbiguous
-        if not (n[0].kind in {nkClosedSymChoice, nkOpenSymChoice, nkIdent} and ambig) and n.len == 2:
+        if not (n[0].kind in nkSymChoices + {nkIdent, nkDotExpr} and ambig) and n.len == 2:
           result = semConv(c, n, flags, expectedType)
-        elif ambig and n.len == 1:
-          errorUseQualifier(c, n.info, s)
         elif n.len == 1:
-          result = semObjConstr(c, n, flags, expectedType)
+          if ambig:
+            errorUseQualifier(c, n.info, s)
+          else:
+            result = semObjConstr(c, n, flags, expectedType)
         elif s.magic == mNone: result = semDirectOp(c, n, flags, expectedType)
         else: result = semMagic(c, n, s, flags, expectedType)
       of skProc, skFunc, skMethod, skConverter, skIterator:
@@ -3261,11 +3435,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
       else:
         #liMessage(n.info, warnUser, renderTree(n));
         result = semIndirectOp(c, n, flags, expectedType)
-    elif (n[0].kind == nkBracketExpr or shouldBeBracketExpr(n)) and
-        isSymChoice(n[0][0]):
-      # indirectOp can deal with explicit instantiations; the fixes
-      # the 'newSeq[T](x)' bug
-      setGenericParams(c, n[0], nil)
+    elif isExplicitGenericCall(c, n): # this modifies `n` if true
       result = semDirectOp(c, n, flags, expectedType)
     elif nfDotField in n.flags:
       result = semDirectOp(c, n, flags, expectedType)
@@ -3330,7 +3500,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
     result = semArrayConstr(c, n, flags, expectedType)
   of nkObjConstr: result = semObjConstr(c, n, flags, expectedType)
   of nkLambdaKinds: result = semProcAux(c, n, skProc, lambdaPragmas, flags)
-  of nkDerefExpr: result = semDeref(c, n)
+  of nkDerefExpr: result = semDeref(c, n, flags)
   of nkAddr:
     result = n
     checkSonsLen(n, 1, c.config)
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index f7da4ea75..80144ccc0 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -420,14 +420,17 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P
       result = newIntNodeT(toInt128(getFloat(a)), n, idgen, g)
     of tyChar, tyUInt..tyUInt64, tyInt..tyInt64:
       var val = a.getOrdValue
-      if check: rangeCheck(n, val, g)
-      result = newIntNodeT(val, n, idgen, g)
       if dstTyp.kind in {tyUInt..tyUInt64}:
+        result = newIntNodeT(maskBytes(val, int getSize(g.config, dstTyp)), n, idgen, g)
         result.transitionIntKind(nkUIntLit)
+      else:
+        if check: rangeCheck(n, val, g)
+        result = newIntNodeT(val, n, idgen, g)
     else:
       result = a
       result.typ = n.typ
-    if check and result.kind in {nkCharLit..nkUInt64Lit}:
+    if check and result.kind in {nkCharLit..nkUInt64Lit} and
+          dstTyp.kind notin {tyUInt..tyUInt64}:
       rangeCheck(n, getInt(result), g)
   of tyFloat..tyFloat64:
     case srcTyp.kind
@@ -701,10 +704,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
     except DivByZeroDefect:
       localError(g.config, n.info, "division by zero")
   of nkAddr:
-    var a = getConstExpr(m, n[0], idgen, g)
-    if a != nil:
-      result = n
-      n[0] = a
+    result = nil # don't fold paths containing nkAddr
   of nkBracket, nkCurly:
     result = copyNode(n)
     for son in n.items:
@@ -750,6 +750,8 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
     if leValueConv(n[1], a) and leValueConv(a, n[2]):
       result = a              # a <= x and x <= b
       result.typ = n.typ
+    elif n.typ.kind in {tyUInt..tyUInt64}:
+      discard "don't check uints"
     else:
       localError(g.config, n.info,
         "conversion from $1 to $2 is invalid" %
@@ -771,7 +773,8 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
   of nkCast:
     var a = getConstExpr(m, n[1], idgen, g)
     if a == nil: return
-    if n.typ != nil and n.typ.kind in NilableTypes:
+    if n.typ != nil and n.typ.kind in NilableTypes and
+        not (n.typ.kind == tyProc and a.typ.kind == tyProc):
       # we allow compile-time 'cast' for pointer types:
       result = a
       result.typ = n.typ
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index 638b4311b..2639aba6c 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -61,6 +61,7 @@ template canOpenSym(s): bool =
 
 proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
                           ctx: var GenericCtx; flags: TSemGenericFlags,
+                          isAmbiguous: bool,
                           fromDotExpr=false): PNode =
   result = nil
   semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
@@ -72,9 +73,15 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
         result.transitionSonsKind(nkClosedSymChoice)
     else:
       result = symChoice(c, n, s, scOpen)
-      if result.kind == nkSym and canOpenSym(result.sym):
-        result.flags.incl nfOpenSym
-        result.typ = nil
+      if canOpenSym(s):
+        if openSym in c.features:
+          if result.kind == nkSym:
+            result = newOpenSym(result)
+          else:
+            result.typ = nil
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
   case s.kind
   of skUnknown:
     # Introduced in this pass! Leave it as an identifier.
@@ -98,13 +105,28 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
     if s.typ != nil and s.typ.kind == tyStatic:
       if s.typ.n != nil:
         result = s.typ.n
+      elif c.inGenericContext > 0 and withinConcept notin flags:
+        # don't leave generic param as identifier node in generic type,
+        # sigmatch will try to instantiate generic type AST without all params
+        # fine to give a symbol node a generic type here since
+        # we are in a generic context and `prepareNode` will be called
+        result = newSymNodeTypeDesc(s, c.idgen, n.info)
+        if canOpenSym(result.sym):
+          if openSym in c.features:
+            result = newOpenSym(result)
+          else:
+            result.flags.incl nfDisabledOpenSym
+            result.typ = nil
       else:
         result = n
     else:
       result = newSymNodeTypeDesc(s, c.idgen, n.info)
       if canOpenSym(result.sym):
-        result.flags.incl nfOpenSym
-        result.typ = nil
+        if openSym in c.features:
+          result = newOpenSym(result)
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
     onUse(n.info, s)
   of skParam:
     result = n
@@ -112,18 +134,41 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
   of skType:
     if (s.typ != nil) and
        (s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
+      if isAmbiguous:
+        # ambiguous types should be symchoices since lookup behaves
+        # differently for them in regular expressions
+        maybeDotChoice(c, n, s, fromDotExpr)
+        return
       result = newSymNodeTypeDesc(s, c.idgen, n.info)
       if canOpenSym(result.sym):
-        result.flags.incl nfOpenSym
-        result.typ = nil
+        if openSym in c.features:
+          result = newOpenSym(result)
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
+    elif c.inGenericContext > 0 and withinConcept notin flags:
+      # don't leave generic param as identifier node in generic type,
+      # sigmatch will try to instantiate generic type AST without all params
+      # fine to give a symbol node a generic type here since
+      # we are in a generic context and `prepareNode` will be called
+      result = newSymNodeTypeDesc(s, c.idgen, n.info)
+      if canOpenSym(result.sym):
+        if openSym in c.features:
+          result = newOpenSym(result)
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
     else:
       result = n
     onUse(n.info, s)
   else:
     result = newSymNode(s, n.info)
     if canOpenSym(result.sym):
-      result.flags.incl nfOpenSym
-      result.typ = nil
+      if openSym in c.features:
+        result = newOpenSym(result)
+      else:
+        result.flags.incl nfDisabledOpenSym
+        result.typ = nil
     onUse(n.info, s)
 
 proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
@@ -145,7 +190,7 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
     elif s.isMixedIn:
       result = symChoice(c, n, s, scForceOpen)
     else:
-      result = semGenericStmtSymbol(c, n, s, ctx, flags)
+      result = semGenericStmtSymbol(c, n, s, ctx, flags, amb)
   # else: leave as nkIdent
 
 proc newDot(n, b: PNode): PNode =
@@ -161,10 +206,11 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
 
   let luf = if withinMixin notin flags: {checkUndeclared, checkModule} else: {checkModule}
 
+  c.isAmbiguous = false
   var s = qualifiedLookUp(c, n, luf)
   if s != nil:
     isMacro = s.kind in {skTemplate, skMacro}
-    result = semGenericStmtSymbol(c, n, s, ctx, flags)
+    result = semGenericStmtSymbol(c, n, s, ctx, flags, c.isAmbiguous)
   else:
     n[0] = semGenericStmt(c, n[0], flags, ctx)
     result = n
@@ -178,24 +224,21 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
       isMacro = s.kind in {skTemplate, skMacro}
       if withinBind in flags or s.id in ctx.toBind:
         if s.kind == skType: # don't put types in sym choice
-          result = newDot(result, semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true))
+          var ambig = false
+          if candidates.len > 1:
+            let s2 = searchInScopes(c, ident, ambig)
+          result = newDot(result, semGenericStmtSymbol(c, n, s, ctx, flags,
+            isAmbiguous = ambig, fromDotExpr = true))
         else:
           result = newDot(result, symChoice(c, n, s, scClosed))
       elif s.isMixedIn:
         result = newDot(result, symChoice(c, n, s, scForceOpen))
       else:
+        var ambig = false
         if s.kind == skType and candidates.len > 1:
-          var ambig = false
-          let s2 = searchInScopes(c, ident, ambig) 
-          if ambig:
-            # this is a type conversion like a.T where T is ambiguous with
-            # other types or routines
-            # in regular code, this never considers a type conversion and
-            # skips to routine overloading
-            # so symchoices are used which behave similarly with type symbols
-            result = newDot(result, symChoice(c, n, s, scForceOpen))
-            return
-        let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true)
+          discard searchInScopes(c, ident, ambig)
+        let syms = semGenericStmtSymbol(c, n, s, ctx, flags,
+          isAmbiguous = ambig, fromDotExpr = true)
         result = newDot(result, syms)
 
 proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
@@ -262,7 +305,9 @@ proc semGenericStmt(c: PContext, n: PNode,
     # check if it is an expression macro:
     checkMinSonsLen(n, 1, c.config)
     let fn = n[0]
+    c.isAmbiguous = false
     var s = qualifiedLookUp(c, fn, {})
+    let ambig = c.isAmbiguous
     if s == nil and
         {withinMixin, withinConcept}*flags == {} and
         fn.kind in {nkIdent, nkAccQuoted} and
@@ -315,7 +360,12 @@ proc semGenericStmt(c: PContext, n: PNode,
       of skType:
         # bad hack for generics:
         if (s.typ != nil) and (s.typ.kind != tyGenericParam):
-          result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info)
+          if ambig:
+            # ambiguous types should be symchoices since lookup behaves
+            # differently for them in regular expressions
+            result[0] = sc
+          else:
+            result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info)
           onUse(fn.info, s)
           first = 1
       else:
@@ -563,7 +613,8 @@ proc semGenericStmt(c: PContext, n: PNode,
       else:
         body = getBody(c.graph, s)
     else: body = n[bodyPos]
-    n[bodyPos] = semGenericStmtScope(c, body, flags, ctx)
+    let bodyFlags = if n.kind == nkTemplateDef: flags + {withinMixin} else: flags
+    n[bodyPos] = semGenericStmtScope(c, body, bodyFlags, ctx)
     closeScope(c)
   of nkPragma, nkPragmaExpr: discard
   of nkExprColonExpr, nkExprEqExpr:
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index e9b46c382..1bc6d31a2 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -53,11 +53,14 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TypeMapping): PS
           if q.typ.kind != tyCompositeTypeClass:
             localError(c.config, a.info, errCannotInstantiateX % s.name.s)
           t = errorType(c)
-      elif t.kind in {tyGenericParam, tyConcept}:
+      elif t.kind in {tyGenericParam, tyConcept, tyFromExpr}:
         localError(c.config, a.info, errCannotInstantiateX % q.name.s)
         t = errorType(c)
-      elif isUnresolvedStatic(t) and c.inGenericContext == 0 and
-          c.matchedConcept == nil:
+      elif isUnresolvedStatic(t) and (q.typ.kind == tyStatic or
+            (q.typ.kind == tyGenericParam and
+              q.typ.genericParamHasConstraints and
+              q.typ.genericConstraint.kind == tyStatic)) and
+          c.inGenericContext == 0 and c.matchedConcept == nil:
         # generic/concept type bodies will try to instantiate static values but
         # won't actually use them
         localError(c.config, a.info, errCannotInstantiateX % q.name.s)
@@ -250,9 +253,15 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
     var typeToFit = resulti
 
     let needsStaticSkipping = resulti.kind == tyFromExpr
+    let needsTypeDescSkipping = resulti.kind == tyTypeDesc and tfUnresolved in resulti.flags
+    if resulti.kind == tyFromExpr:
+      resulti.flags.incl tfNonConstExpr
     result[i] = replaceTypeVarsT(cl, resulti)
     if needsStaticSkipping:
       result[i] = result[i].skipTypes({tyStatic})
+    if needsTypeDescSkipping:
+      result[i] = result[i].skipTypes({tyTypeDesc})
+      typeToFit = result[i]
 
     # ...otherwise, we use the instantiated type in `fitNode`
     if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and
@@ -270,9 +279,10 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
     # call head symbol, because this leads to infinite recursion.
     if oldParam.ast != nil:
       var def = oldParam.ast.copyTree
-      if def.kind in nkCallKinds:
-        for i in 1..<def.len:
-          def[i] = replaceTypeVarsN(cl, def[i], 1)
+      if def.typ.kind == tyFromExpr:
+        def.typ.flags.incl tfNonConstExpr
+      if not isIntLit(def.typ):
+        def = prepareNode(cl, def)
 
       # allow symchoice since node will be fit later
       # although expectedType should cover it
@@ -289,6 +299,8 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
         # the user calls an explicit instantiation of the proc (this is
         # the only way the default value might be inserted).
         param.ast = errorNode(c, def)
+        # we know the node is empty, we need the actual type for error message
+        param.ast.typ = def.typ
       else:
         param.ast = fitNodePostMatch(c, typeToFit, converted)
       param.typ = result[i]
@@ -312,6 +324,22 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
   prc.typ = result
   popInfoContext(c.config)
 
+proc instantiateOnlyProcType(c: PContext, pt: TypeMapping, prc: PSym, info: TLineInfo): PType =
+  # instantiates only the type of a given proc symbol
+  # used by sigmatch for explicit generics
+  # wouldn't be needed if sigmatch could handle complex cases,
+  # examples are in texplicitgenerics
+  # might be buggy, see rest of generateInstance if problems occur
+  let fakeSym = copySym(prc, c.idgen)
+  incl(fakeSym.flags, sfFromGeneric)
+  fakeSym.instantiatedFrom = prc
+  openScope(c)
+  for s in instantiateGenericParamList(c, prc.ast[genericParamsPos], pt):
+    addDecl(c, s)
+  instantiateProcType(c, pt, fakeSym, info)
+  closeScope(c)
+  result = fakeSym.typ
+
 proc fillMixinScope(c: PContext) =
   var p = c.p
   while p != nil:
@@ -342,7 +370,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping,
   # generates an instantiated proc
   if c.instCounter > 50:
     globalError(c.config, info, "generic instantiation too nested")
-  inc(c.instCounter)
+  inc c.instCounter
+  defer: dec c.instCounter
   # careful! we copy the whole AST including the possibly nil body!
   var n = copyTree(fn.ast)
   # NOTE: for access of private fields within generics from a different module
@@ -396,6 +425,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping,
   for _, param in paramTypes(result.typ):
     entry.concreteTypes[i] = param
     inc i
+  #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " ", entry.concreteTypes.len
   if tfTriggersCompileTime in result.typ.flags:
     incl(result.flags, sfCompileTime)
   n[genericParamsPos] = c.graph.emptyNode
@@ -424,7 +454,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping,
     if result.magic notin {mSlice, mTypeOf}:
       # 'toOpenArray' is special and it is allowed to return 'openArray':
       paramsTypeCheck(c, result.typ)
+    #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- NEW PROC!", " ", entry.concreteTypes.len
   else:
+    #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- CACHED! ", typeToString(oldPrc.typ), " ", entry.concreteTypes.len
     result = oldPrc
   popProcCon(c)
   popInfoContext(c.config)
@@ -433,7 +465,6 @@ proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping,
   popOwner(c)
   c.currentScope = oldScope
   discard c.friendModules.pop()
-  dec(c.instCounter)
   c.matchedConcept = oldMatchedConcept
   if result.kind == skMethod: finishMethod(c, result)
 
diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim
index 4aab216c7..727f36470 100644
--- a/compiler/semmacrosanity.nim
+++ b/compiler/semmacrosanity.nim
@@ -51,6 +51,7 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
   of nkObjConstr:
     let x = t.skipTypes(abstractPtrs)
     n.typ = t
+    n[0].typ = t
     for i in 1..<n.len:
       var j = i-1
       let field = x.ithField(j)
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 8db62a9c8..a12e933e7 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -49,8 +49,12 @@ proc semTypeOf(c: PContext; n: PNode): PNode =
     else:
       m = mode.intVal
   result = newNodeI(nkTypeOfExpr, n.info)
+  inc c.inTypeofContext
+  defer: dec c.inTypeofContext # compiles can raise an exception
   let typExpr = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
   result.add typExpr
+  if typExpr.typ.kind == tyFromExpr:
+    typExpr.typ.flags.incl tfNonConstExpr
   result.typ = makeTypeDesc(c, typExpr.typ)
 
 type
@@ -66,7 +70,16 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
   if result.isNil:
     let x = copyTree(n)
     x[0] = newIdentNode(getIdent(c.cache, "[]"), n.info)
-    bracketNotFoundError(c, x)
+    if c.inGenericContext > 0:
+      for i in 0..<n.len:
+        let a = n[i]
+        if a.typ != nil and a.typ.kind in {tyGenericParam, tyFromExpr}:
+          # expression is compiled early in a generic body
+          result = semGenericStmt(c, x)
+          result.typ = makeTypeFromExpr(c, copyTree(result))
+          result.typ.flags.incl tfNonConstExpr
+          return
+    bracketNotFoundError(c, x, flags)
     #localError(c.config, n.info, "could not resolve: " & $n)
     result = errorNode(c, n)
 
@@ -224,8 +237,9 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
   of "rangeBase":
     # return the base type of a range type
     var arg = operand.skipTypes({tyGenericInst})
-    assert arg.kind == tyRange
-    result = getTypeDescNode(c, arg.base, operand.owner, traitCall.info)
+    if arg.kind == tyRange:
+      arg = arg.base
+    result = getTypeDescNode(c, arg, operand.owner, traitCall.info)
   of "isCyclic":
     var operand = operand.skipTypes({tyGenericInst})
     let isCyclic = canFormAcycle(c.graph, operand)
@@ -237,7 +251,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
 proc semTypeTraits(c: PContext, n: PNode): PNode =
   checkMinSonsLen(n, 2, c.config)
   let t = n[1].typ
-  internalAssert c.config, t != nil and t.kind == tyTypeDesc
+  internalAssert c.config, t != nil and t.skipTypes({tyAlias}).kind == tyTypeDesc
   if t.len > 0:
     # This is either a type known to sem or a typedesc
     # param to a regular proc (again, known at instantiation)
diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim
index 96b2d702d..048053115 100644
--- a/compiler/semobjconstr.nim
+++ b/compiler/semobjconstr.nim
@@ -387,10 +387,13 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
     if e != nil:
       result.status = initFull
     elif field.ast != nil:
-      result.status = initUnknown
-      result.defaults.add newTree(nkExprColonExpr, n, field.ast)
+      if efIgnoreDefaults notin flags:
+        result.status = initUnknown
+        result.defaults.add newTree(nkExprColonExpr, n, field.ast)
+      else:
+        result.status = initNone
     else:
-      if efWantNoDefaults notin flags: # cannot compute defaults at the typeRightPass
+      if {efWantNoDefaults, efIgnoreDefaults} * flags == {}: # cannot compute defaults at the typeRightPass
         let defaultExpr = defaultNodeField(c, n, constrCtx.checkDefault)
         if defaultExpr != nil:
           result.status = initUnknown
@@ -443,7 +446,7 @@ proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) =
     assert objType != nil
   if objType.kind == tyObject:
     var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info))
-    let initResult = semConstructTypeAux(c, constrCtx, {efWantNoDefaults})
+    let initResult = semConstructTypeAux(c, constrCtx, {efIgnoreDefaults})
     if constrCtx.missingFields.len > 0:
       localError(c.config, info,
         "The $1 type doesn't have a default value. The following fields must be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)])
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index f611ee8fe..0a160897f 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -11,7 +11,7 @@ import
   ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
   wordrecg, options, guards, lineinfos, semfold, semdata,
   modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling,
-  semstrictfuncs, suggestsymdb
+  semstrictfuncs, suggestsymdb, pushpoppragmas
 
 import std/[tables, intsets, strutils, sequtils]
 
@@ -85,6 +85,7 @@ type
     isInnerProc: bool
     inEnforcedNoSideEffects: bool
     currOptions: TOptions
+    optionsStack: seq[(TOptions, TNoteKinds)]
     config: ConfigRef
     graph: ModuleGraph
     c: PContext
@@ -615,9 +616,16 @@ proc trackPragmaStmt(tracked: PEffects, n: PNode) =
   for i in 0..<n.len:
     var it = n[i]
     let pragma = whichPragma(it)
-    if pragma == wEffects:
+    case pragma
+    of wEffects:
       # list the computed effects up to here:
       listEffects(tracked)
+    of wPush:
+      processPushBackendOption(tracked.c.config, tracked.optionsStack, tracked.currOptions, n, i+1)
+    of wPop:
+      processPopBackendOption(tracked.c.config, tracked.optionsStack, tracked.currOptions)
+    else:
+      discard
 
 template notGcSafe(t): untyped = {tfGcSafe, tfNoSideEffect} * t.flags == {}
 
@@ -1202,7 +1210,7 @@ proc track(tracked: PEffects, n: PNode) =
     if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags:
       tracked.owner.flags.incl sfInjectDestructors
       # bug #15038: ensure consistency
-      if not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ): n.typ = n.sym.typ
+      if n.typ == nil or (not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ)): n.typ = n.sym.typ
   of nkHiddenAddr, nkAddr:
     if n[0].kind == nkSym and isLocalSym(tracked, n[0].sym) and
           n.typ.kind notin {tyVar, tyLent}:
@@ -1594,7 +1602,7 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; c: PContext): TEffects
   result = TEffects(exc: effects[exceptionEffects], tags: effects[tagEffects],
             forbids: effects[forbiddenEffects], owner: s, ownerModule: s.getModule,
             init: @[], locked: @[], graph: g, config: g.config, c: c,
-            currentBlock: 1
+            currentBlock: 1, optionsStack: @[(g.config.options, g.config.notes)]
   )
   result.guards.s = @[]
   result.guards.g = g
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 7ccc29d7e..f5f8fea0c 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -132,17 +132,141 @@ proc semExprBranchScope(c: PContext, n: PNode; expectedType: PType = nil): PNode
   closeScope(c)
 
 const
-  skipForDiscardable = {nkIfStmt, nkIfExpr, nkCaseStmt, nkOfBranch,
-    nkElse, nkStmtListExpr, nkTryStmt, nkFinally, nkExceptBranch,
+  skipForDiscardable = {nkStmtList, nkStmtListExpr,
+    nkOfBranch, nkElse, nkFinally, nkExceptBranch,
     nkElifBranch, nkElifExpr, nkElseExpr, nkBlockStmt, nkBlockExpr,
-    nkHiddenStdConv, nkHiddenDeref}
+    nkHiddenStdConv, nkHiddenSubConv, nkHiddenDeref}
 
 proc implicitlyDiscardable(n: PNode): bool =
-  var n = n
-  while n.kind in skipForDiscardable: n = n.lastSon
-  result = n.kind in nkLastBlockStmts or
-           (isCallExpr(n) and n[0].kind == nkSym and
-           sfDiscardable in n[0].sym.flags)
+  # same traversal as endsInNoReturn
+  template checkBranch(branch) =
+    if not implicitlyDiscardable(branch):
+      return false
+
+  var it = n
+  # skip these beforehand, no special handling needed
+  while it.kind in skipForDiscardable and it.len > 0:
+    it = it.lastSon
+
+  case it.kind
+  of nkIfExpr, nkIfStmt:
+    for branch in it:
+      checkBranch:
+        if branch.len == 2:
+          branch[1]
+        elif branch.len == 1:
+          branch[0]
+        else:
+          raiseAssert "Malformed `if` statement during implicitlyDiscardable"
+    # all branches are discardable
+    result = true
+  of nkCaseStmt:
+    for i in 1 ..< it.len:
+      let branch = it[i]
+      checkBranch:
+        case branch.kind
+        of nkOfBranch:
+          branch[^1]
+        of nkElifBranch:
+          branch[1]
+        of nkElse:
+          branch[0]
+        else:
+          raiseAssert "Malformed `case` statement in implicitlyDiscardable"
+    # all branches are discardable
+    result = true
+  of nkTryStmt:
+    checkBranch(it[0])
+    for i in 1 ..< it.len:
+      let branch = it[i]
+      if branch.kind != nkFinally:
+        checkBranch(branch[^1])
+    # all branches are discardable
+    result = true
+  of nkCallKinds:
+    result = it[0].kind == nkSym and {sfDiscardable, sfNoReturn} * it[0].sym.flags != {}
+  of nkLastBlockStmts:
+    result = true
+  else:
+    result = false
+
+proc endsInNoReturn(n: PNode, returningNode: var PNode; discardableCheck = false): bool =
+  ## check if expr ends the block like raising or call of noreturn procs do
+  result = false # assume it does return
+
+  template checkBranch(branch) =
+    if not endsInNoReturn(branch, returningNode, discardableCheck):
+      # proved a branch returns
+      return false
+
+  var it = n
+  # skip these beforehand, no special handling needed
+  let skips = if discardableCheck: skipForDiscardable else: skipForDiscardable-{nkBlockExpr, nkBlockStmt}
+  while it.kind in skips and it.len > 0:
+    it = it.lastSon
+
+  case it.kind
+  of nkIfExpr, nkIfStmt:
+    var hasElse = false
+    for branch in it:
+      checkBranch:
+        if branch.len == 2:
+          branch[1]
+        elif branch.len == 1:
+          hasElse = true
+          branch[0]
+        else:
+          raiseAssert "Malformed `if` statement during endsInNoReturn"
+    # none of the branches returned
+    result = hasElse # Only truly a no-return when it's exhaustive
+  of nkCaseStmt:
+    let caseTyp = skipTypes(it[0].typ, abstractVar-{tyTypeDesc})
+    # semCase should already have checked for exhaustiveness in this case
+    # effectively the same as having an else
+    var hasElse = caseTyp.shouldCheckCaseCovered()
+
+    # actual noreturn checks
+    for i in 1 ..< it.len:
+      let branch = it[i]
+      checkBranch:
+        case branch.kind
+        of nkOfBranch:
+          branch[^1]
+        of nkElifBranch:
+          branch[1]
+        of nkElse:
+          hasElse = true
+          branch[0]
+        else:
+          raiseAssert "Malformed `case` statement in endsInNoReturn"
+    # Can only guarantee a noreturn if there is an else or it's exhaustive
+    result = hasElse
+  of nkTryStmt:
+    checkBranch(it[0])
+    var lastIndex = it.len - 1
+    if it[lastIndex].kind == nkFinally:
+      # if finally is noreturn, then the entire statement is noreturn
+      if endsInNoReturn(it[lastIndex][^1], returningNode, discardableCheck):
+        return true
+      dec lastIndex
+    for i in 1 .. lastIndex:
+      let branch = it[i]
+      checkBranch(branch[^1])
+    # none of the branches returned
+    result = true
+  of nkLastBlockStmts:
+    result = true
+  of nkCallKinds:
+    result = it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
+    if not result:
+      returningNode = it
+  else:
+    result = false
+    returningNode = it
+
+proc endsInNoReturn(n: PNode): bool =
+  var dummy: PNode = nil
+  result = endsInNoReturn(n, dummy)
 
 proc fixNilType(c: PContext; n: PNode) =
   if isAtom(n):
@@ -160,18 +284,15 @@ proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
     if implicitlyDiscardable(result):
       var n = newNodeI(nkDiscardStmt, result.info, 1)
       n[0] = result
+      # notes that it doesn't transform nodes into discard statements
     elif result.typ.kind != tyError and c.config.cmd != cmdInteractive:
       if result.typ.kind == tyNone:
         localError(c.config, result.info, "expression has no type: " &
                renderTree(result, {renderNoComments}))
       else:
-        var n = result
-        while n.kind in skipForDiscardable:
-          if n.kind == nkTryStmt: n = n[0]
-          else: n = n.lastSon
-
         # Ignore noreturn procs since they don't have a type
-        if n.endsInNoReturn:
+        var n = result
+        if result.endsInNoReturn(n, discardableCheck = true):
           return
 
         var s = "expression '" & $n & "' is of type '" &
@@ -361,7 +482,7 @@ proc identWithin(n: PNode, s: PIdent): bool =
 
 proc semIdentDef(c: PContext, n: PNode, kind: TSymKind, reportToNimsuggest = true): PSym =
   if isTopLevel(c):
-    result = semIdentWithPragma(c, kind, n, {sfExported})
+    result = semIdentWithPragma(c, kind, n, {sfExported}, fromTopLevel = true)
     incl(result.flags, sfGlobal)
     #if kind in {skVar, skLet}:
     #  echo "global variable here ", n.info, " ", result.name.s
@@ -501,15 +622,15 @@ proc setVarType(c: PContext; v: PSym, typ: PType) =
 proc isPossibleMacroPragma(c: PContext, it: PNode, key: PNode): bool =
   # make sure it's not a normal pragma, and calls an identifier
   # considerQuotedIdent below will fail on non-identifiers
-  result = whichPragma(it) == wInvalid and key.kind in nkIdentKinds
+  result = whichPragma(it) == wInvalid and key.kind in nkIdentKinds+{nkDotExpr}
   if result:
     # make sure it's not a user pragma
-    let ident = considerQuotedIdent(c, key)
-    result = strTableGet(c.userPragmas, ident) == nil
+    if key.kind != nkDotExpr:
+      let ident = considerQuotedIdent(c, key)
+      result = strTableGet(c.userPragmas, ident) == nil
     if result:
       # make sure it's not a custom pragma
-      var amb = false
-      let sym = searchInScopes(c, ident, amb)
+      let sym = qualifiedLookUp(c, key, {})
       result = sym == nil or sfCustomPragma notin sym.flags
 
 proc copyExcept(n: PNode, i: int): PNode =
@@ -708,6 +829,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
     var typFlags: TTypeAllowedFlags = {}
 
     var def: PNode = c.graph.emptyNode
+    if typ != nil and typ.kind == tyRange and
+        c.graph.config.isDefined("nimPreviewRangeDefault") and
+        a[^1].kind == nkEmpty:
+      a[^1] = firstRange(c.config, typ)
+
     if a[^1].kind != nkEmpty:
       def = semExprWithType(c, a[^1], {efTypeAllowed}, typ)
 
@@ -859,6 +985,7 @@ proc semConst(c: PContext, n: PNode): PNode =
     var typFlags: TTypeAllowedFlags = {}
 
     # don't evaluate here since the type compatibility check below may add a converter
+    openScope(c)
     var def = semExprWithType(c, a[^1], {efTypeAllowed}, typ)
 
     if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}:
@@ -885,6 +1012,7 @@ proc semConst(c: PContext, n: PNode): PNode =
       if c.matchedConcept != nil:
         typFlags.incl taConcept
       typeAllowedCheck(c, a.info, typ, skConst, typFlags)
+    closeScope(c)
 
     if a.kind == nkVarTuple:
       # generate new section from tuple unpacking and embed it into this one
@@ -1103,7 +1231,7 @@ proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode =
   toResolve.add n[0]
 
   var errors: CandidateErrors = @[]
-  var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {efNoDiagnostics},
+  var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {efNoUndeclared},
                            errors, false)
   if r.state == csMatch:
     var match = r.calleeSym
@@ -1117,8 +1245,6 @@ proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode =
     of skMacro: result = semMacroExpr(c, toExpand, toExpand, match, flags)
     of skTemplate: result = semTemplateExpr(c, toExpand, match, flags)
     else: result = errorNode(c, n[0])
-  elif r.state == csNoMatch:
-    result = errorNode(c, n[0])
   else:
     result = errorNode(c, n[0])
   if result.kind == nkEmpty:
@@ -1589,21 +1715,27 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
   for sk in c.skipTypes:
     discard semTypeNode(c, sk, nil)
   c.skipTypes = @[]
-proc checkForMetaFields(c: PContext; n: PNode) =
-  proc checkMeta(c: PContext; n: PNode; t: PType) =
-    if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
+
+proc checkForMetaFields(c: PContext; n: PNode; hasError: var bool) =
+  proc checkMeta(c: PContext; n: PNode; t: PType; hasError: var bool; parent: PType) =
+    if t != nil and (t.isMetaType or t.kind == tyNone) and tfGenericTypeParam notin t.flags:
       if t.kind == tyBuiltInTypeClass and t.len == 1 and t.elementType.kind == tyProc:
         localError(c.config, n.info, ("'$1' is not a concrete type; " &
           "for a callback without parameters use 'proc()'") % t.typeToString)
+      elif t.kind == tyNone and parent != nil:
+        # TODO: openarray has the `tfGenericTypeParam` flag & generics
+        # TODO: handle special cases (sink etc.) and views
+        localError(c.config, n.info, errTIsNotAConcreteType % parent.typeToString)
       else:
         localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString)
+      hasError = true
 
   if n.isNil: return
   case n.kind
   of nkRecList, nkRecCase:
-    for s in n: checkForMetaFields(c, s)
+    for s in n: checkForMetaFields(c, s, hasError)
   of nkOfBranch, nkElse:
-    checkForMetaFields(c, n.lastSon)
+    checkForMetaFields(c, n.lastSon, hasError)
   of nkSym:
     let t = n.sym.typ
     case t.kind
@@ -1611,9 +1743,9 @@ proc checkForMetaFields(c: PContext; n: PNode) =
        tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink, tyOwned:
       let start = ord(t.kind in {tyGenericInvocation, tyGenericInst})
       for i in start..<t.len:
-        checkMeta(c, n, t[i])
+        checkMeta(c, n, t[i], hasError, t)
     else:
-      checkMeta(c, n, t)
+      checkMeta(c, n, t, hasError, nil)
   else:
     internalAssert c.config, false
 
@@ -1648,13 +1780,16 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
               assert s.typ != nil
               assignType(s.typ, t)
               s.typ.itemId = t.itemId     # same id
-        checkConstructedType(c.config, s.info, s.typ)
-        if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
-          checkForMetaFields(c, s.typ.n)
-
-        # fix bug #5170, bug #17162, bug #15526: ensure locally scoped types get a unique name:
-        if s.typ.kind in {tyEnum, tyRef, tyObject} and not isTopLevel(c):
-          incl(s.flags, sfGenSym)
+        var hasError = false
+        let baseType = s.typ.safeSkipTypes(abstractPtrs)
+        if baseType.kind in {tyObject, tyTuple} and not baseType.n.isNil and
+          (x.kind in {nkObjectTy, nkTupleTy} or
+           (x.kind in {nkRefTy, nkPtrTy} and x.len == 1 and
+           x[0].kind in {nkObjectTy, nkTupleTy})
+          ):
+          checkForMetaFields(c, baseType.n, hasError)
+        if not hasError:
+          checkConstructedType(c.config, s.info, s.typ)
   #instAllTypeBoundOp(c, n.info)
 
 
@@ -2600,20 +2735,23 @@ proc incMod(c: PContext, n: PNode, it: PNode, includeStmtResult: PNode) =
 proc evalInclude(c: PContext, n: PNode): PNode =
   result = newNodeI(nkStmtList, n.info)
   result.add n
+  template checkAs(it: PNode) =
+    if it.kind == nkInfix and it.len == 3:
+      let op = it[0].getPIdent
+      if op != nil and op.id == ord(wAs):
+        localError(c.config, it.info, "Cannot use '" & it[0].renderTree & "' in 'include'.")
   for i in 0..<n.len:
-    var imp: PNode
     let it = n[i]
-    if it.kind == nkInfix and it.len == 3 and it[0].ident.s != "/":
-      localError(c.config, it.info, "Cannot use '" & it[0].ident.s & "' in 'include'.")
-    if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
-      let sep = it[0]
-      let dir = it[1]
-      imp = newNodeI(nkInfix, it.info)
-      imp.add sep
-      imp.add dir
-      imp.add sep # dummy entry, replaced in the loop
-      for x in it[2]:
-        imp[2] = x
+    checkAs(it)
+    if it.kind in {nkInfix, nkPrefix} and it[^1].kind == nkBracket:
+      let lastPos = it.len - 1
+      var imp = copyNode(it)
+      newSons(imp, it.len)
+      for i in 0 ..< lastPos: imp[i] = it[i]
+      imp[lastPos] = imp[0] # dummy entry, replaced in the loop
+      for x in it[lastPos]:
+        checkAs(x)
+        imp[lastPos] = x
         incMod(c, n, imp, result)
     else:
       incMod(c, n, it, result)
@@ -2724,7 +2862,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType =
         let verdict = semConstExpr(c, n[i])
         if verdict == nil or verdict.kind != nkIntLit or verdict.intVal == 0:
           localError(c.config, result.info, "concept predicate failed")
-      of tyUnknown: continue
+      of tyFromExpr: continue
       else: discard
     if n[i].typ == c.enforceVoidContext: #or usesResult(n[i]):
       voidContext = true
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index f2083c85c..817cb6249 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -218,63 +218,98 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
         if k == skParam and c.inTemplateHeader > 0:
           local.flags.incl sfTemplateParam
 
-proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode =
+proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bool): PNode =
   incl(s.flags, sfUsed)
   # bug #12885; ideally sem'checking is performed again afterwards marking
   # the symbol as used properly, but the nfSem mechanism currently prevents
   # that from happening, so we mark the module as used here already:
-  markOwnerModuleAsUsed(c, s)
+  markOwnerModuleAsUsed(c.c, s)
   # we do not call onUse here, as the identifier is not really
   # resolved here. We will fixup the used identifiers later.
   case s.kind
   of skUnknown:
     # Introduced in this pass! Leave it as an identifier.
     result = n
-  of OverloadableSyms-{skTemplate,skMacro}:
-    result = symChoice(c, n, s, scOpen, isField)
-  of skTemplate, skMacro:
-    result = symChoice(c, n, s, scOpen, isField)
-    if result.kind == nkSym:
-      # template/macro symbols might need to be semchecked again
-      # prepareOperand etc don't do this without setting the type to nil
-      result.typ = nil
+  of OverloadableSyms:
+    result = symChoice(c.c, n, s, scOpen, isField)
+    if not isField and result.kind in {nkSym, nkOpenSymChoice}:
+      if openSym in c.c.features:
+        if result.kind == nkSym:
+          result = newOpenSym(result)
+        else:
+          result.typ = nil
+      else:
+        result.flags.incl nfDisabledOpenSym
+        result.typ = nil
   of skGenericParam:
     if isField and sfGenSym in s.flags: result = n
-    else: result = newSymNodeTypeDesc(s, c.idgen, n.info)
+    else:
+      result = newSymNodeTypeDesc(s, c.c.idgen, n.info)
+      if not isField and s.owner != c.owner:
+        if openSym in c.c.features:
+          result = newOpenSym(result)
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
   of skParam:
     result = n
   of skType:
     if isField and sfGenSym in s.flags: result = n
-    else: result = newSymNodeTypeDesc(s, c.idgen, n.info)
+    else:
+      if isAmbiguous:
+        # ambiguous types should be symchoices since lookup behaves
+        # differently for them in regular expressions
+        result = symChoice(c.c, n, s, scOpen, isField)
+      else: result = newSymNodeTypeDesc(s, c.c.idgen, n.info)
+      if not isField and not (s.owner == c.owner and
+          s.typ != nil and s.typ.kind == tyGenericParam) and
+          result.kind in {nkSym, nkOpenSymChoice}:
+        if openSym in c.c.features:
+          if result.kind == nkSym:
+            result = newOpenSym(result)
+          else:
+            result.typ = nil
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
   else:
     if isField and sfGenSym in s.flags: result = n
-    else: result = newSymNode(s, n.info)
+    else:
+      result = newSymNode(s, n.info)
+      if not isField:
+        if openSym in c.c.features:
+          result = newOpenSym(result)
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
     # Issue #12832
     when defined(nimsuggest):
-      suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
+      suggestSym(c.c.graph, n.info, s, c.c.graph.usageSym, false)
     # field access (dot expr) will be handled by builtinFieldAccess
     if not isField:
-      styleCheckUse(c, n.info, s)
+      styleCheckUse(c.c, n.info, s)
 
-proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode =
+proc semRoutineInTemplName(c: var TemplCtx, n: PNode, explicitInject: bool): PNode =
   result = n
   if n.kind == nkIdent:
     let s = qualifiedLookUp(c.c, n, {})
     if s != nil:
-      if s.owner == c.owner and s.kind == skParam:
+      if s.owner == c.owner and (s.kind == skParam or
+          (sfGenSym in s.flags and not explicitInject)):
         incl(s.flags, sfUsed)
         result = newSymNode(s, n.info)
         onUse(n.info, s)
   else:
     for i in 0..<n.safeLen:
-      result[i] = semRoutineInTemplName(c, n[i])
+      result[i] = semRoutineInTemplName(c, n[i], explicitInject)
 
 proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
   result = n
   checkSonsLen(n, bodyPos + 1, c.c.config)
   if n.kind notin nkLambdaKinds:
     # routines default to 'inject':
-    if symBinding(n[pragmasPos]) == spGenSym:
+    let binding = symBinding(n[pragmasPos])
+    if binding == spGenSym:
       let (ident, hasParam) = getIdentReplaceParams(c, n[namePos])
       if not hasParam:
         var s = newGenSym(k, ident, c)
@@ -286,7 +321,7 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
       else:
         n[namePos] = ident
     else:
-      n[namePos] = semRoutineInTemplName(c, n[namePos])
+      n[namePos] = semRoutineInTemplName(c, n[namePos], binding == spInject)
   # open scope for parameters
   openScope(c)
   for i in patternPos..paramsPos-1:
@@ -343,6 +378,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
   case n.kind
   of nkIdent:
     if n.ident.id in c.toInject: return n
+    c.c.isAmbiguous = false
     let s = qualifiedLookUp(c.c, n, {})
     if s != nil:
       if s.owner == c.owner and s.kind == skParam and sfTemplateParam in s.flags:
@@ -360,9 +396,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
         result = newSymNode(s, n.info)
         onUse(n.info, s)
       else:
-        if s.kind in {skType, skVar, skLet, skConst}:
+        if s.kind in {skVar, skLet, skConst}:
           discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
-        result = semTemplSymbol(c.c, n, s, c.noGenSym > 0)
+        result = semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous)
   of nkBind:
     result = semTemplBody(c, n[0])
   of nkBindStmt:
@@ -556,6 +592,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
   of nkDotExpr, nkAccQuoted:
     # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
     # so we use the generic code for nkDotExpr too
+    c.c.isAmbiguous = false
     let s = qualifiedLookUp(c.c, n, {})
     if s != nil:
       # mirror the nkIdent case
@@ -570,9 +607,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
       elif contains(c.toMixin, s.name.id):
         return symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0)
       else:
-        if s.kind in {skType, skVar, skLet, skConst}:
+        if s.kind in {skVar, skLet, skConst}:
           discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
-        return semTemplSymbol(c.c, n, s, c.noGenSym > 0)
+        return semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous)
     if n.kind == nkDotExpr:
       result = n
       result[0] = semTemplBody(c, n[0])
@@ -656,6 +693,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
   pushOwner(c, s)
   openScope(c)
   n[namePos] = newSymNode(s)
+  s.ast = n # for implicitPragmas to use
   pragmaCallable(c, s, n, templatePragmas)
   implicitPragmas(c, s, n.info, templatePragmas)
 
@@ -706,6 +744,17 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
     c: c,
     owner: s
   )
+  # handle default params:
+  for i in 1..<s.typ.n.len:
+    let param = s.typ.n[i].sym
+    if param.ast != nil:
+      # param default values need to be treated like template body:
+      if sfDirty in s.flags:
+        param.ast = semTemplBodyDirty(ctx, param.ast)
+      else:
+        param.ast = semTemplBody(ctx, param.ast)
+      if param.ast.referencesAnotherParam(s):
+        param.ast.flags.incl nfDefaultRefsParam
   if sfDirty in s.flags:
     n[bodyPos] = semTemplBodyDirty(ctx, n[bodyPos])
   else:
@@ -715,11 +764,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
   closeScope(c)
   popOwner(c)
 
-  # set the symbol AST after pragmas, at least. This stops pragma that have
-  # been pushed (implicit) to be explicitly added to the template definition
-  # and misapplied to the body. see #18113
-  s.ast = n
-
   if sfCustomPragma in s.flags:
     if n[bodyPos].kind != nkEmpty:
       localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s)
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index c88795517..113946fef 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -15,7 +15,7 @@ const
   errStringLiteralExpected = "string literal expected"
   errIntLiteralExpected = "integer literal expected"
   errWrongNumberOfVariables = "wrong number of variables"
-  errInvalidOrderInEnumX = "invalid order in enum '$1'"
+  errDuplicateAliasInEnumX = "duplicate value in enum '$1'"
   errOverflowInEnumX = "The enum '$1' exceeds its maximum value ($2)"
   errOrdinalTypeExpected = "ordinal type expected; given: $1"
   errSetTooBig = "set is too large; use `std/sets` for ordinal types with more than 2^16 elements"
@@ -69,6 +69,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
     e: PSym = nil
     base: PType = nil
     identToReplace: ptr PNode = nil
+    counterSet = initPackedSet[BiggestInt]()
   counter = 0
   base = nil
   result = newOrPrevType(tyEnum, prev, c)
@@ -85,6 +86,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
   var hasNull = false
   for i in 1..<n.len:
     if n[i].kind == nkEmpty: continue
+    var useAutoCounter = false
     case n[i].kind
     of nkEnumFieldDef:
       if n[i][0].kind == nkPragmaExpr:
@@ -112,6 +114,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
       of tyString, tyCstring:
         strVal = v
         x = counter
+        useAutoCounter = true
       else:
         if isOrdinalType(v.typ, allowEnumWithHoles=true):
           x = toInt64(getOrdValue(v))
@@ -120,22 +123,30 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
           localError(c.config, v.info, errOrdinalTypeExpected % typeToString(v.typ, preferDesc))
       if i != 1:
         if x != counter: incl(result.flags, tfEnumHasHoles)
-        if x < counter:
-          localError(c.config, n[i].info, errInvalidOrderInEnumX % e.name.s)
-          x = counter
       e.ast = strVal # might be nil
       counter = x
     of nkSym:
       e = n[i].sym
+      useAutoCounter = true
     of nkIdent, nkAccQuoted:
       e = newSymS(skEnumField, n[i], c)
       identToReplace = addr n[i]
+      useAutoCounter = true
     of nkPragmaExpr:
       e = newSymS(skEnumField, n[i][0], c)
       pragma(c, e, n[i][1], enumFieldPragmas)
       identToReplace = addr n[i][0]
+      useAutoCounter = true
     else:
       illFormedAst(n[i], c.config)
+
+    if useAutoCounter:
+      while counter in counterSet and counter != high(typeof(counter)):
+        inc counter
+      counterSet.incl counter
+    elif counterSet.containsOrIncl(counter):
+      localError(c.config, n[i].info, errDuplicateAliasInEnumX % e.name.s)
+
     e.typ = result
     e.position = int(counter)
     let symNode = newSymNode(e)
@@ -464,6 +475,13 @@ proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType =
     let t = semTypeNode(c, it, nil)
     addSonSkipIntLitChecked(c, result, t, it, c.idgen)
 
+proc firstRange(config: ConfigRef, t: PType): PNode =
+  if t.skipModifier().kind in tyFloat..tyFloat64:
+    result = newFloatNode(nkFloatLit, firstFloat(t))
+  else:
+    result = newIntNode(nkIntLit, firstOrd(config, t))
+  result.typ = t
+
 proc semTuple(c: PContext, n: PNode, prev: PType): PType =
   var typ: PType
   result = newOrPrevType(tyTuple, prev, c)
@@ -480,8 +498,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
     elif a[^2].kind != nkEmpty:
       typ = semTypeNode(c, a[^2], nil)
       if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
-        a[^1] = newIntNode(nkIntLit, firstOrd(c.config, typ))
-        a[^1].typ = typ
+        a[^1] = firstRange(c.config, typ)
         hasDefaultField = true
     else:
       localError(c.config, a.info, errTypeExpected)
@@ -529,7 +546,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
     result = newSymG(kind, n, c)
 
 proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
-                        allowed: TSymFlags): PSym =
+                        allowed: TSymFlags, fromTopLevel = false): PSym =
   if n.kind == nkPragmaExpr:
     checkSonsLen(n, 2, c.config)
     result = semIdentVis(c, kind, n[0], allowed)
@@ -544,11 +561,15 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
     else: discard
   else:
     result = semIdentVis(c, kind, n, allowed)
+    let invalidPragmasForPush = if fromTopLevel and sfWasGenSym notin result.flags:
+      {}
+    else:
+      {wExportc, wExportCpp, wDynlib}
     case kind
     of skField: implicitPragmas(c, result, n.info, fieldPragmas)
-    of skVar:   implicitPragmas(c, result, n.info, varPragmas)
-    of skLet:   implicitPragmas(c, result, n.info, letPragmas)
-    of skConst: implicitPragmas(c, result, n.info, constPragmas)
+    of skVar:   implicitPragmas(c, result, n.info, varPragmas-invalidPragmasForPush)
+    of skLet:   implicitPragmas(c, result, n.info, letPragmas-invalidPragmasForPush)
+    of skConst: implicitPragmas(c, result, n.info, constPragmas-invalidPragmasForPush)
     else: discard
 
 proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) =
@@ -565,9 +586,14 @@ proc semBranchRange(c: PContext, n, a, b: PNode, covered: var Int128): PNode =
   let bc = semConstExpr(c, b)
   if ac.kind in {nkStrLit..nkTripleStrLit} or bc.kind in {nkStrLit..nkTripleStrLit}:
     localError(c.config, b.info, "range of string is invalid")
-  let at = fitNode(c, n[0].typ, ac, ac.info).skipConvTakeType
-  let bt = fitNode(c, n[0].typ, bc, bc.info).skipConvTakeType
-
+  var at = fitNode(c, n[0].typ, ac, ac.info).skipConvTakeType
+  var bt = fitNode(c, n[0].typ, bc, bc.info).skipConvTakeType
+  # the calls to fitNode may introduce calls to converters
+  # mirrored with semCaseBranch for single elements
+  if at.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
+    at = semConstExpr(c, at)
+  if bt.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
+    bt = semConstExpr(c, bt)
   result = newNodeI(nkRange, a.info)
   result.add(at)
   result.add(bt)
@@ -598,6 +624,8 @@ proc semCaseBranch(c: PContext, n, branch: PNode, branchIndex: int,
     var b = branch[i]
     if b.kind == nkRange:
       branch[i] = b
+      # same check as in semBranchRange for exhaustiveness
+      covered = covered + getOrdValue(b[1]) + 1 - getOrdValue(b[0])
     elif isRange(b):
       branch[i] = semCaseBranchRange(c, n, b, covered)
     else:
@@ -613,8 +641,8 @@ proc semCaseBranch(c: PContext, n, branch: PNode, branchIndex: int,
         checkMinSonsLen(n, 1, c.config)
         var tmp = fitNode(c, n[0].typ, r, r.info)
         # the call to fitNode may introduce a call to a converter
-        if tmp.kind == nkHiddenCallConv or
-            (tmp.kind == nkHiddenStdConv and n[0].typ.kind == tyCstring):
+        # mirrored with semBranchRange
+        if tmp.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
           tmp = semConstExpr(c, tmp)
         branch[i] = skipConv(tmp)
         inc(covered)
@@ -768,6 +796,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
   of nkRecWhen:
     var a = copyTree(n)
     var branch: PNode = nil   # the branch to take
+    var cannotResolve = false # no branch should be taken
     for i in 0..<a.len:
       var it = a[i]
       if it == nil: illFormedAst(n, c.config)
@@ -785,24 +814,30 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
           let e = semExprWithType(c, it[0], {efDetermineType})
           if e.typ.kind == tyFromExpr:
             it[0] = makeStaticExpr(c, e)
+            cannotResolve = true
           else:
             it[0] = forceBool(c, e)
+            let val = getConstExpr(c.module, it[0], c.idgen, c.graph)
+            if val == nil or val.kind != nkIntLit:
+              cannotResolve = true
+            elif not cannotResolve and val.intVal != 0 and branch == nil:
+              branch = it[1]
       of nkElse:
         checkSonsLen(it, 1, c.config)
-        if branch == nil: branch = it[0]
+        if branch == nil and not cannotResolve: branch = it[0]
         idx = 0
       else: illFormedAst(n, c.config)
-      if c.inGenericContext > 0:
+      if c.inGenericContext > 0 and cannotResolve:
         # use a new check intset here for each branch:
         var newCheck: IntSet = check
         var newPos = pos
         var newf = newNodeI(nkRecList, n.info)
         semRecordNodeAux(c, it[idx], newCheck, newPos, newf, rectype, hasCaseFields)
         it[idx] = if newf.len == 1: newf[0] else: newf
-    if c.inGenericContext > 0:
-      father.add a
-    elif branch != nil:
+    if branch != nil:
       semRecordNodeAux(c, branch, check, pos, father, rectype, hasCaseFields)
+    elif cannotResolve:
+      father.add a
     elif father.kind in {nkElse, nkOfBranch}:
       father.add newNodeI(nkRecList, n.info)
   of nkRecCase:
@@ -831,8 +866,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
     else:
       typ = semTypeNode(c, n[^2], nil)
       if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
-        n[^1] = newIntNode(nkIntLit, firstOrd(c.config, typ))
-        n[^1].typ = typ
+        n[^1] = firstRange(c.config, typ)
         hasDefaultField = true
       propagateToOwner(rectype, typ)
     var fieldOwner = if c.inGenericContext > 0: c.getCurrOwner
@@ -849,8 +883,8 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
       f.options = c.config.options
       if fieldOwner != nil and
          {sfImportc, sfExportc} * fieldOwner.flags != {} and
-         not hasCaseFields and f.loc.r == "":
-        f.loc.r = rope(f.name.s)
+         not hasCaseFields and f.loc.snippet == "":
+        f.loc.snippet = rope(f.name.s)
         f.flags.incl {sfImportc, sfExportc} * fieldOwner.flags
       inc(pos)
       if containsOrIncl(check, f.name.id):
@@ -1151,11 +1185,11 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
         paramType[i] = t
         result = paramType
 
-  of tyAlias, tyOwned, tySink:
+  of tyAlias, tyOwned:
     result = recurse(paramType.base)
 
   of tySequence, tySet, tyArray, tyOpenArray,
-     tyVar, tyLent, tyPtr, tyRef, tyProc:
+     tyVar, tyLent, tyPtr, tyRef, tyProc, tySink:
     # XXX: this is a bit strange, but proc(s: seq)
     # produces tySequence(tyGenericParam, tyNone).
     # This also seems to be true when creating aliases
@@ -1262,7 +1296,7 @@ proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType =
     result = semTypeNode(c, n[0], nil)
     constraint = semNodeKindConstraints(n, c.config, 1)
   elif n.kind == nkCall and
-      n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice} and
+      n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice, nkOpenSym} and
       considerQuotedIdent(c, n[0]).s == "{}":
     result = semTypeNode(c, n[1], nil)
     constraint = semNodeKindConstraints(n, c.config, 2)
@@ -1292,6 +1326,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
   result = newProcType(c, n.info, prev)
   var check = initIntSet()
   var counter = 0
+  template isCurrentlyGeneric: bool =
+    # genericParams might update as implicit generic params are added
+    genericParams != nil and genericParams.len > 0
 
   for i in 1..<n.len:
     var a = n[i]
@@ -1312,7 +1349,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
       hasDefault = a[^1].kind != nkEmpty
 
     if hasType:
+      let isGeneric = isCurrentlyGeneric()
+      inc c.inGenericContext, ord(isGeneric)
       typ = semParamType(c, a[^2], constraint)
+      dec c.inGenericContext, ord(isGeneric)
       # TODO: Disallow typed/untyped in procs in the compiler/stdlib
       if kind in {skProc, skFunc} and (typ.kind == tyTyped or typ.kind == tyUntyped):
         if not isMagic(getCurrOwner(c)):
@@ -1332,15 +1372,26 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
           "either use ';' (semicolon) or explicitly write each default value")
         message(c.config, a.info, warnImplicitDefaultValue, msg)
       block determineType:
-        var defTyp = typ
-        if genericParams != nil and genericParams.len > 0:
-          defTyp = nil
-          def = semGenericStmt(c, def)
-          if hasUnresolvedArgs(c, def):
+        var canBeVoid = false
+        if kind == skTemplate:
+          if typ != nil and typ.kind == tyUntyped:
+            # don't do any typechecking or assign a type for
+            # `untyped` parameter default value
+            break determineType
+          elif hasUnresolvedArgs(c, def):
+            # template default value depends on other parameter
+            # don't do any typechecking
             def.typ = makeTypeFromExpr(c, def.copyTree)
             break determineType
-
-        def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, defTyp)
+          elif typ != nil and typ.kind == tyTyped:
+            canBeVoid = true
+        let isGeneric = isCurrentlyGeneric()
+        inc c.inGenericContext, ord(isGeneric)
+        if canBeVoid:
+          def = semExpr(c, def, {efDetermineType, efAllowSymChoice}, typ)
+        else:
+          def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, typ)
+        dec c.inGenericContext, ord(isGeneric)
         if def.referencesAnotherParam(getCurrOwner(c)):
           def.flags.incl nfDefaultRefsParam
 
@@ -1359,7 +1410,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
           typ = newTypeS(tyTypeDesc, c, newTypeS(tyNone, c))
           typ.flags.incl tfCheckedForDestructor
 
-      else:
+      elif def.typ != nil and def.typ.kind != tyFromExpr: # def.typ can be void
         # if def.typ != nil and def.typ.kind != tyNone:
         # example code that triggers it:
         # proc sort[T](cmp: proc(a, b: T): int = cmp)
@@ -1412,11 +1463,12 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
       onDef(a[j].info, arg)
       a[j] = newSymNode(arg)
 
-  var r: PType =
-    if n[0].kind != nkEmpty:
-      semTypeNode(c, n[0], nil)
-    else:
-      nil
+  var r: PType = nil
+  if n[0].kind != nkEmpty:
+    let isGeneric = isCurrentlyGeneric()
+    inc c.inGenericContext, ord(isGeneric)
+    r = semTypeNode(c, n[0], nil)
+    dec c.inGenericContext, ord(isGeneric)
 
   if r != nil and kind in {skMacro, skTemplate} and r.kind == tyTyped:
     # XXX: To implement the proposed change in the warning, just
@@ -1469,7 +1521,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
           result.flags.excl tfHasMeta
       result.n.typ = r
 
-  if genericParams != nil and genericParams.len > 0:
+  if isCurrentlyGeneric():
     for n in genericParams:
       if {sfUsed, sfAnon} * n.sym.flags == {}:
         result.flags.incl tfUnresolved
@@ -1626,7 +1678,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
     recomputeFieldPositions(tx, tx.n, position)
 
 proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
-  if typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody} and prev != nil:
+  if prev != nil and (prev.kind == tyGenericBody or
+      typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody}):
     result = newTypeS(tyAlias, c)
     result.rawAddSon typeExpr
     result.sym = prev.sym
@@ -1664,6 +1717,10 @@ proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
         # unnecessary new type creation
         let alias = maybeAliasType(c, result, prev)
         if alias != nil: result = alias
+  elif n.typ.kind == tyFromExpr and c.inGenericContext > 0:
+    # sometimes not possible to distinguish type from value in generic body,
+    # for example `T.Foo`, so both are handled under `tyFromExpr`
+    result = n.typ
   else:
     localError(c.config, n.info, "expected type, but got: " & n.renderTree)
     result = errorType(c)
@@ -1749,12 +1806,15 @@ proc applyTypeSectionPragmas(c: PContext; pragmas, operand: PNode): PNode =
       discard "builtin pragma"
     else:
       trySuggestPragmas(c, key)
-      let ident = considerQuotedIdent(c, key)
-      if strTableGet(c.userPragmas, ident) != nil:
+      let ident =
+        if key.kind in nkIdentKinds:
+          considerQuotedIdent(c, key)
+        else:
+          nil
+      if ident != nil and strTableGet(c.userPragmas, ident) != nil:
         discard "User-defined pragma"
       else:
-        var amb = false
-        let sym = searchInScopes(c, ident, amb)
+        let sym = qualifiedLookUp(c, key, {})
         # XXX: What to do here if amb is true?
         if sym != nil and sfCustomPragma in sym.flags:
           discard "Custom user pragma"
@@ -1821,10 +1881,14 @@ proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
 
 proc semTypeOf(c: PContext; n: PNode; prev: PType): PType =
   openScope(c)
+  inc c.inTypeofContext
+  defer: dec c.inTypeofContext # compiles can raise an exception
   let t = semExprWithType(c, n, {efInTypeof})
   closeScope(c)
   fixupTypeOf(c, prev, t)
   result = t.typ
+  if result.kind == tyFromExpr:
+    result.flags.incl tfNonConstExpr
 
 proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
   openScope(c)
@@ -1835,10 +1899,14 @@ proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
       localError(c.config, n.info, "typeof: cannot evaluate 'mode' parameter at compile-time")
     else:
       m = mode.intVal
+  inc c.inTypeofContext
+  defer: dec c.inTypeofContext # compiles can raise an exception
   let t = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
   closeScope(c)
   fixupTypeOf(c, prev, t)
   result = t.typ
+  if result.kind == tyFromExpr:
+    result.flags.incl tfNonConstExpr
 
 proc semTypeIdent(c: PContext, n: PNode): PSym =
   if n.kind == nkSym:
@@ -1926,11 +1994,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
   of nkTupleConstr: result = semAnonTuple(c, n, prev)
   of nkCallKinds:
     let x = n[0]
-    let ident = case x.kind
-                of nkIdent: x.ident
-                of nkSym: x.sym.name
-                of nkClosedSymChoice, nkOpenSymChoice: x[0].sym.name
-                else: nil
+    let ident = x.getPIdent
     if ident != nil and ident.s == "[]":
       let b = newNodeI(nkBracketExpr, n.info)
       for i in 1..<n.len: b.add(n[i])
@@ -2020,20 +2084,21 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       elif op.id == ord(wType):
         checkSonsLen(n, 2, c.config)
         result = semTypeOf(c, n[1], prev)
-      elif op.s == "typeof" and n[0].kind == nkSym and n[0].sym.magic == mTypeOf:
+      elif op.s == "typeof" and (
+          (n[0].kind == nkSym and n[0].sym.magic == mTypeOf) or
+          (n[0].kind == nkOpenSym and n[0][0].sym.magic == mTypeOf)):
         result = semTypeOf2(c, n, prev)
       elif op.s == "owned" and optOwnedRefs notin c.config.globalOptions and n.len == 2:
         result = semTypeExpr(c, n[1], prev)
       else:
-        if c.inGenericContext > 0 and n.kind == nkCall:
-          let n = semGenericStmt(c, n)
-          result = makeTypeFromExpr(c, n.copyTree)
-        else:
-          result = semTypeExpr(c, n, prev)
+        result = semTypeExpr(c, n, prev)
   of nkWhenStmt:
     var whenResult = semWhen(c, n, false)
     if whenResult.kind == nkStmtList: whenResult.transitionSonsKind(nkStmtListType)
-    result = semTypeNode(c, whenResult, prev)
+    if whenResult.kind == nkWhenStmt:
+      result = whenResult.typ
+    else:
+      result = semTypeNode(c, whenResult, prev)
   of nkBracketExpr:
     checkMinSonsLen(n, 2, c.config)
     var head = n[0]
@@ -2078,6 +2143,12 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     of mRef: result = semAnyRef(c, n, tyRef, prev)
     of mPtr: result = semAnyRef(c, n, tyPtr, prev)
     of mTuple: result = semTuple(c, n, prev)
+    of mBuiltinType:
+      case s.name.s
+      of "lent": result = semAnyRef(c, n, tyLent, prev)
+      of "sink": result = semAnyRef(c, n, tySink, prev)
+      of "owned": result = semAnyRef(c, n, tyOwned, prev)
+      else: result = semGeneric(c, n, s, prev)
     else: result = semGeneric(c, n, s, prev)
   of nkDotExpr:
     let typeExpr = semExpr(c, n)
@@ -2107,7 +2178,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       if s.kind != skError: localError(c.config, n.info, errTypeExpected)
       result = newOrPrevType(tyError, prev, c)
     elif s.kind == skParam and s.typ.kind == tyTypeDesc:
-      internalAssert c.config, s.typ.base.kind != tyNone and prev == nil
+      internalAssert c.config, s.typ.base.kind != tyNone
       result = s.typ.base
     elif prev == nil:
       result = s.typ
@@ -2131,7 +2202,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
         if s.kind == skType:
           s.typ
         else:
-          internalAssert c.config, s.typ.base.kind != tyNone and prev == nil
+          internalAssert c.config, s.typ.base.kind != tyNone
           s.typ.base
       let alias = maybeAliasType(c, t, prev)
       if alias != nil:
@@ -2192,6 +2263,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
   of nkType: result = n.typ
   of nkStmtListType: result = semStmtListType(c, n, prev)
   of nkBlockType: result = semBlockType(c, n, prev)
+  of nkOpenSym: result = semTypeNode(c, n[0], prev)
   else:
     result = semTypeExpr(c, n, prev)
     when false:
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index f0ce8d76f..759e8e6ab 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -118,7 +118,12 @@ proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
   result = replaceTypeVarsTAux(cl, t)
   checkMetaInvariants(cl, result)
 
-proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
+proc prepareNode*(cl: var TReplTypeVars, n: PNode): PNode =
+  ## instantiates a given generic expression, not a type node
+  if n.kind == nkSym and n.sym.kind == skType and
+      n.sym.typ != nil and n.sym.typ.kind == tyGenericBody:
+    # generic body types are allowed as user expressions, see #24090
+    return n
   let t = replaceTypeVarsT(cl, n.typ)
   if t != nil and t.kind == tyStatic and t.n != nil:
     return if tfUnresolved in t.flags: prepareNode(cl, t.n)
@@ -131,11 +136,68 @@ proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
         replaceTypeVarsS(cl, n.sym, result.typ)
       else:
         replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ))
-  let isCall = result.kind in nkCallKinds
-  for i in 0..<n.safeLen:
-    # XXX HACK: ``f(a, b)``, avoid to instantiate `f`
-    if isCall and i == 0: result.add(n[i])
-    else: result.add(prepareNode(cl, n[i]))
+  # we need to avoid trying to instantiate nodes that can have uninstantiated
+  # types, like generic proc symbols or raw generic type symbols
+  case n.kind
+  of nkSymChoices:
+    # don't try to instantiate symchoice symbols, they can be
+    # generic procs which the compiler will think are uninstantiated
+    # because their type will contain uninstantiated params
+    for i in 0..<n.len:
+      result.add(n[i])
+  of nkCallKinds:
+    # don't try to instantiate call names since they may be generic proc syms
+    # also bracket expressions can turn into calls with symchoice [] and
+    # we need to not instantiate the Generic in Generic[int]
+    # exception exists for the call name being a dot expression since
+    # dot expressions need their LHS instantiated
+    assert n.len != 0
+    # avoid instantiating generic proc symbols, refine condition if needed:
+    let ignoreFirst = n[0].kind notin {nkDotExpr, nkBracketExpr} + nkCallKinds
+    let name = n[0].getPIdent
+    let ignoreSecond = name != nil and name.s == "[]" and n.len > 1 and
+      # generic type instantiation:
+      ((n[1].typ != nil and n[1].typ.kind == tyTypeDesc) or
+        # generic proc instantiation:
+        (n[1].kind == nkSym and n[1].sym.isGenericRoutineStrict))
+    if ignoreFirst:
+      result.add(n[0])
+    else:
+      result.add(prepareNode(cl, n[0]))
+    if n.len > 1:
+      if ignoreSecond:
+        result.add(n[1])
+      else:
+        result.add(prepareNode(cl, n[1]))
+    for i in 2..<n.len:
+      result.add(prepareNode(cl, n[i]))
+  of nkBracketExpr:
+    # don't instantiate Generic body type in expression like Generic[T]
+    # exception exists for the call name being a dot expression since
+    # dot expressions need their LHS instantiated
+    assert n.len != 0
+    let ignoreFirst = n[0].kind != nkDotExpr and
+      # generic type instantiation:
+      ((n[0].typ != nil and n[0].typ.kind == tyTypeDesc) or
+        # generic proc instantiation:
+        (n[0].kind == nkSym and n[0].sym.isGenericRoutineStrict))
+    if ignoreFirst:
+      result.add(n[0])
+    else:
+      result.add(prepareNode(cl, n[0]))
+    for i in 1..<n.len:
+      result.add(prepareNode(cl, n[i]))
+  of nkDotExpr:
+    # don't try to instantiate RHS of dot expression, it can outright be
+    # undeclared, but definitely instantiate LHS
+    assert n.len >= 2
+    result.add(prepareNode(cl, n[0]))
+    result.add(n[1])
+    for i in 2..<n.len:
+      result.add(prepareNode(cl, n[i]))
+  else:
+    for i in 0..<n.safeLen:
+      result.add(prepareNode(cl, n[i]))
 
 proc isTypeParam(n: PNode): bool =
   # XXX: generic params should use skGenericParam instead of skType
@@ -218,6 +280,9 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT
   if n == nil: return
   result = copyNode(n)
   if n.typ != nil:
+    if n.typ.kind == tyFromExpr:
+      # type of node should not be evaluated as a static value
+      n.typ.flags.incl tfNonConstExpr
     result.typ = replaceTypeVarsT(cl, n.typ)
     checkMetaInvariants(cl, result.typ)
   case n.kind
@@ -230,7 +295,8 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT
         replaceTypeVarsS(cl, n.sym, result.typ)
       else:
         replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ))
-    if result.sym.typ.kind == tyVoid:
+    # sym type can be nil if was gensym created by macro, see #24048
+    if result.sym.typ != nil and result.sym.typ.kind == tyVoid:
       # don't add the 'void' field
       result = newNodeI(nkRecList, n.info)
   of nkRecWhen:
@@ -569,6 +635,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
       result.kind = tyUserTypeClassInst
 
   of tyGenericBody:
+    if cl.allowMetaTypes: return
     localError(
       cl.c.config,
       cl.info,
@@ -587,13 +654,18 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
     assert t.n.typ != t
     var n = prepareNode(cl, t.n)
     if n.kind != nkEmpty:
-      n = cl.c.semConstExpr(cl.c, n)
+      if tfNonConstExpr in t.flags:
+        n = cl.c.semExprWithType(cl.c, n, flags = {efInTypeof})
+      else:
+        n = cl.c.semConstExpr(cl.c, n)
     if n.typ.kind == tyTypeDesc:
       # XXX: sometimes, chained typedescs enter here.
       # It may be worth investigating why this is happening,
       # because it may cause other bugs elsewhere.
       result = n.typ.skipTypes({tyTypeDesc})
       # result = n.typ.base
+    elif tfNonConstExpr in t.flags:
+      result = n.typ
     else:
       if n.typ.kind != tyStatic and n.kind != nkType:
         # XXX: In the future, semConstExpr should
@@ -619,8 +691,31 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
     elif t.elementType.kind != tyNone:
       result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.elementType))
 
-  of tyUserTypeClass, tyStatic:
+  of tyUserTypeClass:
     result = t
+  
+  of tyStatic:
+    if cl.c.matchedConcept != nil:
+      # allow concepts to not instantiate statics for now
+      # they can't always infer them
+      return
+    if not containsGenericType(t) and (t.n == nil or t.n.kind in nkLiterals):
+      # no need to instantiate
+      return
+    bailout()
+    result = instCopyType(cl, t)
+    cl.localCache[t.itemId] = result
+    for i in FirstGenericParamAt..<result.kidsLen:
+      var r = result[i]
+      if r != nil:
+        r = replaceTypeVarsT(cl, r)
+        result[i] = r
+        propagateToOwner(result, r)
+    result.n = replaceTypeVarsN(cl, result.n)
+    if not cl.allowMetaTypes and result.n != nil and
+        result.base.kind != tyNone:
+      result.n = cl.c.semConstExpr(cl.c, result.n)
+      result.n.typ = result.base
 
   of tyGenericInst, tyUserTypeClassInst:
     bailout()
@@ -641,7 +736,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
 
       for i, resulti in result.ikids:
         if resulti != nil:
-          if resulti.kind == tyGenericBody:
+          if resulti.kind == tyGenericBody and not cl.allowMetaTypes:
             localError(cl.c.config, if t.sym != nil: t.sym.info else: cl.info,
               "cannot instantiate '" &
               typeToString(result[i], preferDesc) &
@@ -706,6 +801,14 @@ proc replaceTypesInBody*(p: PContext, pt: TypeMapping, n: PNode;
   result = replaceTypeVarsN(cl, n, expectedType = expectedType)
   popInfoContext(p.config)
 
+proc prepareTypesInBody*(p: PContext, pt: TypeMapping, n: PNode;
+                         owner: PSym = nil): PNode =
+  var typeMap = initLayeredTypeMap(pt)
+  var cl = initTypeVars(p, typeMap, n.info, owner)
+  pushInfoContext(p.config, n.info)
+  result = prepareNode(cl, n)
+  popInfoContext(p.config)
+
 when false:
   # deadcode
   proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode;
diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim
index 1b75f6be6..d8dfe1828 100644
--- a/compiler/sighashes.nim
+++ b/compiler/sighashes.nim
@@ -55,6 +55,8 @@ proc hashSym(c: var MD5Context, s: PSym) =
       c &= it.name.s
       c &= "."
       it = it.owner
+    c &= "#"
+    c &= s.disamb
 
 proc hashTypeSym(c: var MD5Context, s: PSym; conf: ConfigRef) =
   if sfAnon in s.flags or s.kind == skGenericParam:
@@ -69,6 +71,8 @@ proc hashTypeSym(c: var MD5Context, s: PSym; conf: ConfigRef) =
       c &= it.name.s
       c &= "."
       it = it.owner
+    c &= "#"
+    c &= s.disamb
 
 proc hashTree(c: var MD5Context, n: PNode; flags: set[ConsiderFlag]; conf: ConfigRef) =
   if n == nil:
@@ -154,9 +158,9 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
     # is actually safe without an infinite recursion check:
     if t.sym != nil:
       if {sfCompilerProc} * t.sym.flags != {}:
-        doAssert t.sym.loc.r != ""
+        doAssert t.sym.loc.snippet != ""
         # The user has set a specific name for this type
-        c &= t.sym.loc.r
+        c &= t.sym.loc.snippet
       elif CoOwnerSig in flags:
         c.hashTypeSym(t.sym, conf)
       else:
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 30ce24500..6ea2c7bb5 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -23,7 +23,8 @@ when defined(nimPreviewSlimSystem):
 type
   MismatchKind* = enum
     kUnknown, kAlreadyGiven, kUnknownNamedParam, kTypeMismatch, kVarNeeded,
-    kMissingParam, kExtraArg, kPositionalAlreadyGiven
+    kMissingParam, kExtraArg, kPositionalAlreadyGiven,
+    kGenericParamTypeMismatch, kMissingGenericParam, kExtraGenericParam
 
   MismatchInfo* = object
     kind*: MismatchKind # reason for mismatch
@@ -59,8 +60,8 @@ type
     magic*: TMagic           # magic of operation
     baseTypeMatch: bool      # needed for conversions from T to openarray[T]
                              # for example
-    fauxMatch*: TTypeKind    # the match was successful only due to the use
-                             # of error or wildcard (unknown) types.
+    matchedErrorType*: bool  # match is considered successful after matching
+                             # error type to avoid cascading errors
                              # this is used to prevent instantiations.
     genericConverter*: bool  # true if a generic converter needs to
                              # be instantiated
@@ -80,7 +81,8 @@ type
                               # or when the explain pragma is used. may be
                               # triggered with an idetools command in the
                               # future.
-    inheritancePenalty: int   # to prefer closest father object type
+                              # to prefer closest father object type
+    inheritancePenalty: int
     firstMismatch*: MismatchInfo # mismatch info for better error messages
     diagnosticsEnabled*: bool
 
@@ -95,19 +97,18 @@ type
 
 const
   isNilConversion = isConvertible # maybe 'isIntConv' fits better?
+  maxInheritancePenalty = high(int) div 2
 
-proc markUsed*(c: PContext; info: TLineInfo, s: PSym)
+proc markUsed*(c: PContext; info: TLineInfo, s: PSym; checkStyle = true)
 proc markOwnerModuleAsUsed*(c: PContext; s: PSym)
 
-template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone
-
 proc initCandidateAux(ctx: PContext,
                       callee: PType): TCandidate {.inline.} =
   result = TCandidate(c: ctx, exactMatches: 0, subtypeMatches: 0,
                       convMatches: 0, intConvMatches: 0, genericMatches: 0,
                       state: csEmpty, firstMismatch: MismatchInfo(),
                       callee: callee, call: nil, baseTypeMatch: false,
-                      genericConverter: false, inheritancePenalty: 0
+                      genericConverter: false, inheritancePenalty: -1
   )
 
 proc initCandidate*(ctx: PContext, callee: PType): TCandidate =
@@ -129,6 +130,110 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} =
       echo "binding ", key, " -> ", val
   idTablePut(c.bindings, key, val.skipIntLit(c.c.idgen))
 
+proc typeRel*(c: var TCandidate, f, aOrig: PType,
+              flags: TTypeRelFlags = {}): TTypeRelation
+
+proc matchGenericParam(m: var TCandidate, formal: PType, n: PNode) =
+  var arg = n.typ
+  if m.c.inGenericContext > 0:
+    # don't match yet-unresolved generic instantiations
+    while arg != nil and arg.kind == tyGenericParam:
+      arg = idTableGet(m.bindings, arg)
+    if arg == nil or arg.containsUnresolvedType:
+      m.state = csNoMatch
+      return
+  # fix up the type to get ready to match formal:
+  var formalBase = formal
+  while formalBase.kind == tyGenericParam and
+      formalBase.genericParamHasConstraints:
+    formalBase = formalBase.genericConstraint
+  if formalBase.kind == tyStatic and arg.kind != tyStatic:
+    # maybe call `paramTypesMatch` here, for now be conservative
+    if n.kind in nkSymChoices: n.flags.excl nfSem
+    let evaluated = m.c.semTryConstExpr(m.c, n, formalBase.skipTypes({tyStatic}))
+    if evaluated != nil:
+      arg = newTypeS(tyStatic, m.c, son = evaluated.typ)
+      arg.n = evaluated
+  elif formalBase.kind == tyTypeDesc:
+    if arg.kind != tyTypeDesc:
+      arg = makeTypeDesc(m.c, arg)
+  else:
+    arg = arg.skipTypes({tyTypeDesc})
+  let tm = typeRel(m, formal, arg)
+  if tm in {isNone, isConvertible}:
+    m.state = csNoMatch
+    m.firstMismatch.kind = kGenericParamTypeMismatch
+    return
+
+proc matchGenericParams*(m: var TCandidate, binding: PNode, callee: PSym) =
+  ## matches explicit generic instantiation `binding` against generic params of
+  ## proc symbol `callee`
+  ## state is set to `csMatch` if all generic params match, `csEmpty` if
+  ## implicit generic parameters are missing (matches but cannot instantiate),
+  ## `csNoMatch` if a constraint fails or param count doesn't match
+  let c = m.c
+  let typeParams = callee.ast[genericParamsPos]
+  let paramCount = typeParams.len
+  let bindingCount = binding.len-1
+  if bindingCount > paramCount:
+    m.state = csNoMatch
+    m.firstMismatch.kind = kExtraGenericParam
+    m.firstMismatch.arg = paramCount + 1
+    return
+  for i in 1..bindingCount:
+    matchGenericParam(m, typeParams[i-1].typ, binding[i])
+    if m.state == csNoMatch:
+      m.firstMismatch.arg = i
+      m.firstMismatch.formal = typeParams[i-1].sym
+      return
+  # not enough generic params given, check if remaining have defaults:
+  for i in bindingCount ..< paramCount:
+    let param = typeParams[i]
+    assert param.kind == nkSym
+    let paramSym = param.sym
+    if paramSym.ast != nil:
+      matchGenericParam(m, param.typ, paramSym.ast)
+      if m.state == csNoMatch:
+        m.firstMismatch.arg = i + 1
+        m.firstMismatch.formal = paramSym
+        return
+    elif tfImplicitTypeParam in paramSym.typ.flags:
+      # not a mismatch, but can't create sym
+      m.state = csEmpty
+      return
+    else:
+      m.state = csNoMatch
+      m.firstMismatch.kind = kMissingGenericParam
+      m.firstMismatch.arg = i + 1
+      m.firstMismatch.formal = paramSym
+      return
+  m.state = csMatch
+
+proc copyingEraseVoidParams(m: TCandidate, t: var PType) =
+  ## if `t` is a proc type with void parameters, copies it and erases them
+  assert t.kind == tyProc
+  let original = t
+  var copied = false
+  for i in 1 ..< original.len:
+    var f = original[i]
+    var isVoidParam = f.kind == tyVoid
+    if not isVoidParam:
+      let prev = idTableGet(m.bindings, f)
+      if prev != nil: f = prev
+      isVoidParam = f.kind == tyVoid
+    if isVoidParam:
+      if not copied:
+        # keep first i children
+        t = copyType(original, m.c.idgen, t.owner)
+        t.setSonsLen(i)
+        t.n = copyNode(original.n)
+        t.n.sons = original.n.sons
+        t.n.sons.setLen(i)
+        copied = true
+    elif copied:
+      t.add(f)
+      t.n.add(original.n[i])
+
 proc initCandidate*(ctx: PContext, callee: PSym,
                     binding: PNode, calleeScope = -1,
                     diagnosticsEnabled = false): TCandidate =
@@ -143,17 +248,20 @@ proc initCandidate*(ctx: PContext, callee: PSym,
   result.magic = result.calleeSym.magic
   result.bindings = initTypeMapping()
   if binding != nil and callee.kind in routineKinds:
-    var typeParams = callee.ast[genericParamsPos]
-    for i in 1..min(typeParams.len, binding.len-1):
-      var formalTypeParam = typeParams[i-1].typ
-      var bound = binding[i].typ
-      if bound != nil:
-        if formalTypeParam.kind == tyTypeDesc:
-          if bound.kind != tyTypeDesc:
-            bound = makeTypeDesc(ctx, bound)
-        else:
-          bound = bound.skipTypes({tyTypeDesc})
-        put(result, formalTypeParam, bound)
+    matchGenericParams(result, binding, callee)
+    let genericMatch = result.state
+    if genericMatch != csNoMatch:
+      result.state = csEmpty
+      if genericMatch == csMatch: # csEmpty if not fully instantiated
+        # instantiate the type, emulates old compiler behavior
+        # wouldn't be needed if sigmatch could handle complex cases,
+        # examples are in texplicitgenerics
+        # might be buggy, see rest of generateInstance if problems occur
+        let typ = ctx.instantiateOnlyProcType(ctx, result.bindings, callee, binding.info)
+        result.callee = typ
+      else:
+        # createThread[void] requires this if the above branch is removed:
+        copyingEraseVoidParams(result, result.callee)
 
 proc newCandidate*(ctx: PContext, callee: PSym,
                    binding: PNode, calleeScope = -1): TCandidate =
@@ -176,9 +284,6 @@ proc copyCandidate(dest: var TCandidate, src: TCandidate) =
   dest.baseTypeMatch = src.baseTypeMatch
   dest.bindings = src.bindings
 
-proc typeRel*(c: var TCandidate, f, aOrig: PType,
-              flags: TTypeRelFlags = {}): TTypeRelation
-
 proc checkGeneric(a, b: TCandidate): int =
   let c = a.c
   let aa = a.callee
@@ -287,6 +392,15 @@ proc writeMatches*(c: TCandidate) =
   echo "  conv matches: ", c.convMatches
   echo "  inheritance: ", c.inheritancePenalty
 
+proc cmpInheritancePenalty(a, b: int): int =
+  var eb = b
+  var ea = a
+  if b < 0:
+    eb = maxInheritancePenalty  # defacto max penalty
+  if a < 0:
+    ea = maxInheritancePenalty
+  eb - ea
+
 proc cmpCandidates*(a, b: TCandidate, isFormal=true): int =
   result = a.exactMatches - b.exactMatches
   if result != 0: return
@@ -298,8 +412,7 @@ proc cmpCandidates*(a, b: TCandidate, isFormal=true): int =
   if result != 0: return
   result = a.convMatches - b.convMatches
   if result != 0: return
-  # the other way round because of other semantics:
-  result = b.inheritancePenalty - a.inheritancePenalty
+  result = cmpInheritancePenalty(a.inheritancePenalty, b.inheritancePenalty)
   if result != 0: return
   if isFormal:
     # check for generic subclass relation
@@ -328,16 +441,27 @@ template describeArgImpl(c: PContext, n: PNode, i: int, startIdx = 1; prefer = p
     result.add renderTree(n[i][0])
     result.add ": "
     if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
-      # XXX we really need to 'tryExpr' here!
-      arg = c.semOperand(c, n[i][1])
-      n[i].typ = arg.typ
-      n[i][1] = arg
+      arg = c.semTryExpr(c, n[i][1])
+      if arg == nil:
+        arg = n[i][1]
+        arg.typ = newTypeS(tyUntyped, c)
+      else:
+        if arg.typ == nil:
+          arg.typ = newTypeS(tyVoid, c)
+        n[i].typ = arg.typ
+        n[i][1] = arg
   else:
     if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo, nkElse,
                                           nkOfBranch, nkElifBranch,
                                           nkExceptBranch}:
-      arg = c.semOperand(c, n[i])
-      n[i] = arg
+      arg = c.semTryExpr(c, n[i])
+      if arg == nil:
+        arg = n[i]
+        arg.typ = newTypeS(tyUntyped, c)
+      else:
+        if arg.typ == nil:
+          arg.typ = newTypeS(tyVoid, c)
+        n[i] = arg
   if arg.typ != nil and arg.typ.kind == tyError: return
   result.add argTypeToString(arg, prefer)
 
@@ -361,6 +485,7 @@ proc concreteType(c: TCandidate, t: PType; f: PType = nil): PType =
     else: result = t
   of tyGenericParam, tyAnything, tyConcept:
     result = t
+    if c.isNoCall: return
     while true:
       result = idTableGet(c.bindings, t)
       if result == nil:
@@ -388,9 +513,16 @@ proc handleRange(c: PContext, f, a: PType, min, max: TTypeKind): TTypeRelation =
     let k = ab.kind
     let nf = c.config.normalizeKind(f.kind)
     let na = c.config.normalizeKind(k)
-    if k == f.kind: result = isSubrange
-    elif k == tyInt and f.kind in {tyRange, tyInt..tyInt64,
-                                   tyUInt..tyUInt64} and
+    if k == f.kind:
+      # `a` is a range type matching its base type
+      # see very bottom for range types matching different types
+      if isIntLit(ab):
+        # range type can only give isFromIntLit for base type
+        result = isFromIntLit
+      else:
+        result = isSubrange
+    elif a.kind == tyInt and f.kind in {tyRange, tyInt..tyInt64,
+                                        tyUInt..tyUInt64} and
         isIntLit(ab) and getInt(ab.n) >= firstOrd(nil, f) and
                          getInt(ab.n) <= lastOrd(nil, f):
       # passing 'nil' to firstOrd/lastOrd here as type checking rules should
@@ -588,10 +720,9 @@ proc recordRel(c: var TCandidate, f, a: PType, flags: TTypeRelFlags): TTypeRelat
     let firstField = if f.kind == tyTuple: 0
                      else: 1
     for _, ff, aa in tupleTypePairs(f, a):
-      let oldInheritancePenalty = c.inheritancePenalty
       var m = typeRel(c, ff, aa, flags)
       if m < isSubtype: return isNone
-      if m == isSubtype and c.inheritancePenalty > oldInheritancePenalty:
+      if m == isSubtype and aa.kind != tyNil and c.inheritancePenalty > -1:
         # we can't process individual element type conversions from a
         # type conversion for the whole tuple
         # subtype relations need type conversions when inheritance is used
@@ -677,6 +808,8 @@ proc procParamTypeRel(c: var TCandidate; f, a: PType): TTypeRelation =
 proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
   case a.kind
   of tyProc:
+    var f = f
+    copyingEraseVoidParams(c, f)
     if f.signatureLen != a.signatureLen: return
     result = isEqual      # start with maximum; also correct for no
                           # params at all
@@ -786,7 +919,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
             param.typ.flags.incl tfInferrableStatic
           else:
             param.ast = typ.n
-        of tyUnknown:
+        of tyFromExpr:
           param = paramSym skVar
           param.typ = typ.exactReplica
           #copyType(typ, c.idgen, typ.owner)
@@ -865,6 +998,7 @@ proc maybeSkipDistinct(m: TCandidate; t: PType, callee: PSym): PType =
 
 proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
                             allowUnresolved = false,
+                            allowCalls = false,
                             expectedType: PType = nil): PNode =
   # Consider this example:
   #   type Value[N: static[int]] = object
@@ -874,7 +1008,7 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
   # This proc is used to evaluate such static expressions.
   let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil,
                                         allowMetaTypes = allowUnresolved)
-  if instantiated.kind in nkCallKinds:
+  if not allowCalls and instantiated.kind in nkCallKinds:
     return nil
   result = c.c.semExpr(c.c, instantiated)
 
@@ -946,7 +1080,8 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =
 
     else: discard
 
-  elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and lhs.typ.n == nil:
+  elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and
+      (lhs.typ.n == nil or idTableGet(c.bindings, lhs.typ) == nil):
     var inferred = newTypeS(tyStatic, c.c, lhs.typ.elementType)
     inferred.n = newIntNode(nkIntLit, rhs)
     put(c, lhs.typ, inferred)
@@ -990,9 +1125,21 @@ proc inferStaticsInRange(c: var TCandidate,
     doInferStatic(lowerBound, getInt(upperBound) + 1 - lengthOrd(c.c.config, concrete))
 
 template subtypeCheck() =
-  if result <= isSubrange and f.last.skipTypes(abstractInst).kind in {
-      tyRef, tyPtr, tyVar, tyLent, tyOwned}:
+  case result
+  of isIntConv:
     result = isNone
+  of isSubrange:
+    discard # XXX should be isNone with preview define, warnings
+  of isConvertible:
+    if f.last.skipTypes(abstractInst).kind != tyOpenArray:
+      # exclude var openarray which compiler supports
+      result = isNone
+  of isSubtype:
+    if f.last.skipTypes(abstractInst).kind in {
+        tyRef, tyPtr, tyVar, tyLent, tyOwned}:
+      # compiler can't handle subtype conversions with pointer indirection
+      result = isNone
+  else: discard
 
 proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
   # this proc is always called for a pair of matching types
@@ -1144,6 +1291,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
     if prev == nil: body
     else: return typeRel(c, prev, a, flags)
 
+  if c.c.inGenericContext > 0 and not c.isNoCall and
+      (tfUnresolved in a.flags or a.kind in tyTypeClasses):
+    # cheap check for unresolved arg, not nested
+    return isNone
+
   case a.kind
   of tyOr:
     # XXX: deal with the current dual meaning of tyGenericParam
@@ -1196,10 +1348,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
         return isGeneric
   of tyFromExpr:
     if c.c.inGenericContext > 0:
-      # generic type bodies can sometimes compile call expressions
-      # prevent expressions with unresolved types from
-      # being passed as parameters
-      return isNone
+      if not c.isNoCall:
+        # generic type bodies can sometimes compile call expressions
+        # prevent expressions with unresolved types from
+        # being passed as parameters
+        return isNone
+      else:
+        # Foo[templateCall(T)] shouldn't fail early if Foo has a constraint
+        # and we can't evaluate `templateCall(T)` yet
+        return isGeneric
   else: discard
 
   case f.kind
@@ -1260,13 +1417,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
     subtypeCheck()
   of tyArray:
     a = reduceToBase(a)
-    case a.kind
-    of tyArray:
+    if a.kind == tyArray:
       var fRange = f.indexType
       var aRange = a.indexType
       if fRange.kind in {tyGenericParam, tyAnything}:
         var prev = idTableGet(c.bindings, fRange)
         if prev == nil:
+          if typeRel(c, fRange, aRange) == isNone:
+            return isNone
           put(c, fRange, a.indexType)
           fRange = a
         else:
@@ -1279,7 +1437,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
         result = isGeneric
       else:
         result = typeRel(c, ff, aa, flags)
-
       if result < isGeneric:
         if nimEnableCovariance and
            trNoCovariance notin flags and
@@ -1290,6 +1447,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
           return isNone
 
       if fRange.rangeHasUnresolvedStatic:
+        if aRange.kind in {tyGenericParam} and aRange.reduceToBase() == aRange:
+          return
         return inferStaticsInRange(c, fRange, a)
       elif c.c.matchedConcept != nil and aRange.rangeHasUnresolvedStatic:
         return inferStaticsInRange(c, aRange, f)
@@ -1298,12 +1457,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
       else:
         if lengthOrd(c.c.config, fRange) != lengthOrd(c.c.config, aRange):
           result = isNone
-    else: discard
-  of tyUncheckedArray:
-    if a.kind == tyUncheckedArray:
-      result = typeRel(c, elementType(f), elementType(a), flags)
-      if result < isGeneric: result = isNone
-    else: discard
   of tyOpenArray, tyVarargs:
     # varargs[untyped] is special too but handled earlier. So we only need to
     # handle varargs[typed]:
@@ -1344,9 +1497,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
             typeRel(c, base(f), base(a), flags) >= isGeneric:
           result = isConvertible
     else: discard
-  of tySequence:
-    case a.kind
-    of tySequence:
+  of tySequence, tyUncheckedArray:
+    if a.kind == f.kind:
       if (f[0].kind != tyGenericParam) and (a.elementType.kind == tyEmpty):
         result = isSubtype
       else:
@@ -1361,8 +1513,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
             result = isSubtype
           else:
             result = isNone
-    of tyNil: result = isNone
-    else: discard
+    elif a.kind == tyNil:
+      result = isNone
   of tyOrdinal:
     if isOrdinalType(a):
       var x = if a.kind == tyOrdinal: a.elementType else: a
@@ -1388,12 +1540,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
         reduceToBase(a)
     if effectiveArgType.kind == tyObject:
       if sameObjectTypes(f, effectiveArgType):
+        c.inheritancePenalty = if tfFinal in f.flags: -1 else: 0
         result = isEqual
         # elif tfHasMeta in f.flags: result = recordRel(c, f, a)
       elif trIsOutParam notin flags:
-        var depth = isObjectSubtype(c, effectiveArgType, f, nil)
-        if depth > 0:
-          inc(c.inheritancePenalty, depth)
+        c.inheritancePenalty = isObjectSubtype(c, effectiveArgType, f, nil)
+        if c.inheritancePenalty > 0:
           result = isSubtype
   of tyDistinct:
     a = a.skipTypes({tyOwned, tyGenericInst, tyRange})
@@ -1409,11 +1561,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
       else:
         result = typeRel(c, f[0], a[0], flags)
         if result < isGeneric:
-          if result <= isConvertible:
-            result = isNone
-          elif tfIsConstructor notin a.flags:
-            # set constructors are a bit special...
+          if tfIsConstructor notin a.flags:
+            # set['a'..'z'] and set[char] have different representations
             result = isNone
+          else:
+            # but we can convert individual elements of the constructor
+            result = isConvertible
   of tyPtr, tyRef:
     a = reduceToBase(a)
     if a.kind == f.kind:
@@ -1562,7 +1715,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
         if aAsObject.kind == tyObject and trIsOutParam notin flags:
           let baseType = aAsObject.base
           if baseType != nil:
-            c.inheritancePenalty += 1
+            inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0)
             let ret = typeRel(c, f, baseType, flags)
             return if ret in {isEqual,isGeneric}: isSubtype else: ret
 
@@ -1659,7 +1812,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
           depth = -1
 
       if depth >= 0:
-        c.inheritancePenalty += depth
+        inc c.inheritancePenalty, depth + int(c.inheritancePenalty < 0)
         # bug #4863: We still need to bind generic alias crap, so
         # we cannot return immediately:
         result = if depth == 0: isGeneric else: isSubtype
@@ -1677,19 +1830,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
     considerPreviousT:
       result = isNone
       let oldInheritancePenalty = c.inheritancePenalty
-      var maxInheritance = 0
+      var minInheritance = maxInheritancePenalty
       for branch in f.kids:
-        c.inheritancePenalty = 0
+        c.inheritancePenalty = -1
         let x = typeRel(c, branch, aOrig, flags)
-        maxInheritance = max(maxInheritance, c.inheritancePenalty)
-        # 'or' implies maximum matching result:
-        if x > result: result = x
+        if x >= result:
+          if  c.inheritancePenalty > -1:
+            minInheritance = min(minInheritance, c.inheritancePenalty)
+          result = x
       if result >= isIntConv:
+        if minInheritance < maxInheritancePenalty:
+          c.inheritancePenalty = oldInheritancePenalty + minInheritance
         if result > isGeneric: result = isGeneric
         bindingRet result
       else:
         result = isNone
-      c.inheritancePenalty = oldInheritancePenalty + maxInheritance
   of tyNot:
     considerPreviousT:
       if typeRel(c, f.elementType, aOrig, flags) != isNone:
@@ -1799,18 +1954,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
       else:
         # check if 'T' has a constraint as in 'proc p[T: Constraint](x: T)'
         if f.len > 0 and f[0].kind != tyNone:
-          let oldInheritancePenalty = c.inheritancePenalty
           result = typeRel(c, f[0], a, flags + {trDontBind, trBindGenericParam})
           if doBindGP and result notin {isNone, isGeneric}:
             let concrete = concreteType(c, a, f)
             if concrete == nil: return isNone
             put(c, f, concrete)
-          # bug #6526
           if result in {isEqual, isSubtype}:
-            # 'T: Class' is a *better* match than just 'T'
-            # but 'T: Subclass' is even better:
-            c.inheritancePenalty = oldInheritancePenalty - c.inheritancePenalty -
-                                  100 * ord(result == isEqual)
             result = isGeneric
         elif a.kind == tyTypeDesc:
           # somewhat special typing rule, the following is illegal:
@@ -1843,16 +1992,29 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
     elif x.kind == tyGenericParam:
       result = isGeneric
     else:
+      # This is the bound type - can't benifit from these tallies
+      let
+        inheritancePenaltyOld = c.inheritancePenalty
       result = typeRel(c, x, a, flags) # check if it fits
+      c.inheritancePenalty = inheritancePenaltyOld
       if result > isGeneric: result = isGeneric
   of tyStatic:
     let prev = idTableGet(c.bindings, f)
     if prev == nil:
       if aOrig.kind == tyStatic:
-        if f.base.kind notin {tyNone, tyGenericParam}:
+        if c.c.inGenericContext > 0 and aOrig.n == nil and not c.isNoCall:
+          # don't match unresolved static value to static param to avoid
+          # faulty instantiations in calls in generic bodies
+          # but not for generic invocations as they only check constraints
+          result = isNone
+        elif f.base.kind notin {tyNone, tyGenericParam}:
           result = typeRel(c, f.base, a, flags)
           if result != isNone and f.n != nil:
-            if not exprStructuralEquivalent(f.n, aOrig.n):
+            var r = tryResolvingStaticExpr(c, f.n)
+            if r == nil: r = f.n
+            if not exprStructuralEquivalent(r, aOrig.n) and
+                not (aOrig.n != nil and aOrig.n.kind == nkIntLit and
+                  inferStaticParam(c, r, aOrig.n.intVal)):
               result = isNone
         elif f.base.kind == tyGenericParam:
           # Handling things like `type A[T; Y: static T] = object`
@@ -1904,8 +2066,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
       # proc foo(T: typedesc, x: T)
       # when `f` is an unresolved typedesc, `a` could be any
       # type, so we should not perform this check earlier
-      if c.c.inGenericContext > 0 and
-          a.skipTypes({tyTypeDesc}).kind == tyGenericParam:
+      if c.c.inGenericContext > 0 and a.containsUnresolvedType:
         # generic type bodies can sometimes compile call expressions
         # prevent unresolved generic parameters from being passed to procs as
         # typedesc parameters
@@ -1934,28 +2095,36 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
     if aOrig != nil:
       put(c, f, aOrig)
     result = isGeneric
-  of tyProxy:
+  of tyError:
     result = isEqual
   of tyFromExpr:
     # fix the expression, so it contains the already instantiated types
     if f.n == nil or f.n.kind == nkEmpty: return isGeneric
-    let reevaluated = tryResolvingStaticExpr(c, f.n)
-    if reevaluated == nil:
+    if c.c.inGenericContext > 0:
+      # need to delay until instantiation
+      # also prevent infinite recursion below
+      return isNone
+    inc c.c.inGenericContext # to generate tyFromExpr again if unresolved
+    # use prepareNode for consistency with other tyFromExpr in semtypinst:
+    let instantiated = prepareTypesInBody(c.c, c.bindings, f.n)
+    let reevaluated = c.c.semExpr(c.c, instantiated).typ
+    dec c.c.inGenericContext
+    case reevaluated.kind
+    of tyFromExpr:
+      # not resolved
       result = isNone
-      return
-    case reevaluated.typ.kind
     of tyTypeDesc:
-      result = typeRel(c, a, reevaluated.typ.base, flags)
+      result = typeRel(c, reevaluated.base, a, flags)
     of tyStatic:
-      result = typeRel(c, a, reevaluated.typ.base, flags)
-      if result != isNone and reevaluated.typ.n != nil:
-        if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n):
+      result = typeRel(c, reevaluated.base, a, flags)
+      if result != isNone and reevaluated.n != nil:
+        if not exprStructuralEquivalent(aOrig.n, reevaluated.n):
           result = isNone
     else:
       # bug #14136: other types are just like 'tyStatic' here:
-      result = typeRel(c, a, reevaluated.typ, flags)
-      if result != isNone and reevaluated.typ.n != nil:
-        if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n):
+      result = typeRel(c, reevaluated, a, flags)
+      if result != isNone and reevaluated.n != nil:
+        if not exprStructuralEquivalent(aOrig.n, reevaluated.n):
           result = isNone
   of tyNone:
     if a.kind == tyNone: result = isEqual
@@ -1994,7 +2163,7 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
                   c: PContext): PNode =
   result = newNodeI(kind, arg.info)
   if containsGenericType(f):
-    if not m.hasFauxMatch:
+    if not m.matchedErrorType:
       result.typ = getInstantiatedType(c, arg, m, f).skipTypes({tySink})
     else:
       result.typ = errorType(c)
@@ -2015,6 +2184,81 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
   else:
     result.add arg
 
+proc convertLiteral(kind: TNodeKind, c: PContext, m: TCandidate; n: PNode, newType: PType): PNode =
+  # based off changeType but generates implicit conversions instead
+  template addConsiderNil(s, node) =
+    let val = node
+    if val.isNil: return nil
+    s.add(val)
+  case n.kind
+  of nkCurly:
+    result = copyNode(n)
+    for i in 0..<n.len:
+      if n[i].kind == nkRange:
+        var x = copyNode(n[i])
+        x.addConsiderNil convertLiteral(kind, c, m, n[i][0], elemType(newType))
+        x.addConsiderNil convertLiteral(kind, c, m, n[i][1], elemType(newType))
+        result.add x
+      else:
+        result.addConsiderNil convertLiteral(kind, c, m, n[i], elemType(newType))
+    result.typ = newType
+    return
+  of nkBracket:
+    result = copyNode(n)
+    for i in 0..<n.len:
+      result.addConsiderNil convertLiteral(kind, c, m, n[i], elemType(newType))
+    result.typ = newType
+    return
+  of nkPar, nkTupleConstr:
+    let tup = newType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
+    if tup.kind == tyTuple:
+      result = copyNode(n)
+      if n.len > 0 and n[0].kind == nkExprColonExpr:
+        # named tuple?
+        for i in 0..<n.len:
+          var name = n[i][0]
+          if name.kind != nkSym:
+            #globalError(c.config, name.info, "invalid tuple constructor")
+            return nil
+          if tup.n != nil:
+            var f = getSymFromList(tup.n, name.sym.name)
+            if f == nil:
+              #globalError(c.config, name.info, "unknown identifier: " & name.sym.name.s)
+              return nil
+            result.addConsiderNil convertLiteral(kind, c, m, n[i][1], f.typ)
+          else:
+            result.addConsiderNil convertLiteral(kind, c, m, n[i][1], tup[i])
+      else:
+        for i in 0..<n.len:
+          result.addConsiderNil convertLiteral(kind, c, m, n[i], tup[i])
+      result.typ = newType
+      return
+  of nkCharLit..nkUInt64Lit:
+    if n.kind != nkUInt64Lit and not sameTypeOrNil(n.typ, newType) and isOrdinalType(newType):
+      let value = n.intVal
+      if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
+        return nil
+      result = copyNode(n)
+      result.typ = newType
+      return
+  of nkFloatLit..nkFloat64Lit:
+    if newType.skipTypes(abstractVarRange-{tyTypeDesc}).kind == tyFloat:
+      if not floatRangeCheck(n.floatVal, newType):
+        return nil
+      result = copyNode(n)
+      result.typ = newType
+      return
+  of nkSym:
+    if n.sym.kind == skEnumField and not sameTypeOrNil(n.sym.typ, newType) and isOrdinalType(newType):
+      let value = n.sym.position
+      if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
+        return nil
+      result = copyNode(n)
+      result.typ = newType
+      return
+  else: discard
+  return implicitConv(kind, newType, n, m, c)
+
 proc isLValue(c: PContext; n: PNode, isOutParam = false): bool {.inline.} =
   let aa = isAssignable(nil, n)
   case aa
@@ -2152,6 +2396,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
          a.n == nil and
          tfGenericTypeParam notin a.flags:
         return newNodeIT(nkType, argOrig.info, makeTypeFromExpr(c, arg))
+    elif a.kind == tyFromExpr and c.inGenericContext > 0:
+      # don't try to evaluate
+      discard
     elif arg.kind != nkEmpty:
       var evaluated = c.semTryConstExpr(c, arg)
       if evaluated != nil:
@@ -2223,7 +2470,20 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
     if f.skipTypes({tyRange}).kind in {tyInt, tyUInt}:
       inc(m.convMatches)
     inc(m.convMatches)
-    result = implicitConv(nkHiddenStdConv, f, arg, m, c)
+    if skipTypes(f, abstractVar-{tyTypeDesc}).kind == tySet:
+      if tfIsConstructor in a.flags and arg.kind == nkCurly:
+        # we marked the set as convertible only because the arg is a literal
+        # in which case we individually convert each element
+        let t =
+          if containsGenericType(f):
+            getInstantiatedType(c, arg, m, f).skipTypes({tySink})
+          else:
+            f.skipTypes({tySink})
+        result = convertLiteral(nkHiddenStdConv, c, m, arg, t)
+      else:
+        result = nil
+    else:
+      result = implicitConv(nkHiddenStdConv, f, arg, m, c)
   of isIntConv:
     # I'm too lazy to introduce another ``*matches`` field, so we conflate
     # ``isIntConv`` and ``isIntLit`` here:
@@ -2256,8 +2516,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
     inc(m.genericMatches)
     if arg.typ == nil:
       result = arg
-    elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or
-         m.inheritancePenalty > oldInheritancePenalty:
+    elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or cmpInheritancePenalty(oldInheritancePenalty, m.inheritancePenalty) > 0:
       result = implicitConv(nkHiddenSubConv, f, arg, m, c)
     elif arg.typ.isEmptyContainer:
       result = arg.copyTree
@@ -2283,13 +2542,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
       result = implicitConv(nkHiddenSubConv, f, arg, m, c)
   of isNone:
     # do not do this in ``typeRel`` as it then can't infer T in ``ref T``:
-    if a.kind in {tyProxy, tyUnknown}:
-      if a.kind == tyUnknown and c.inGenericContext > 0:
-        # don't bother with fauxMatch mechanism in generic type,
-        # reject match, typechecking will be delayed to instantiation
-        return nil
+    if a.kind == tyFromExpr: return nil
+    elif a.kind == tyError:
       inc(m.genericMatches)
-      m.fauxMatch = a.kind
+      m.matchedErrorType = true
       return arg
     elif a.kind == tyVoid and f.matchesVoidProc and argOrig.kind == nkStmtList:
       # lift do blocks without params to lambdas
@@ -2358,7 +2614,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
     result = paramTypesMatchAux(m, f, a, arg, argOrig)
   else:
     # symbol kinds that don't participate in symchoice type disambiguation:
-    let matchSet = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage, skType}
+    let matchSet = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
 
     var best = -1
     result = arg
@@ -2403,6 +2659,12 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
         if arg[i].sym.kind in matchSet:
           copyCandidate(z, m)
           z.callee = arg[i].typ
+          if arg[i].sym.kind == skType and z.callee.kind != tyTypeDesc:
+            # creating the symchoice with the type sym having typedesc type
+            # breaks a lot of stuff, so we make the typedesc type here
+            # mirrored from `newSymNodeTypeDesc`
+            z.callee = newType(tyTypeDesc, c.idgen, arg[i].sym.owner)
+            z.callee.addSonSkipIntLit(arg[i].sym.typ, c.idgen)
           if tfUnresolved in z.callee.flags: continue
           z.calleeSym = arg[i].sym
           z.calleeScope = cmpScopes(m.c, arg[i].sym)
@@ -2453,7 +2715,7 @@ proc setSon(father: PNode, at: int, son: PNode) =
 # we are allowed to modify the calling node in the 'prepare*' procs:
 proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
   if formal.kind == tyUntyped and formal.len != 1:
-    # {tyTypeDesc, tyUntyped, tyTyped, tyProxy}:
+    # {tyTypeDesc, tyUntyped, tyTyped, tyError}:
     # a.typ == nil is valid
     result = a
   elif a.typ.isNil:
@@ -2491,7 +2753,7 @@ proc arrayConstr(c: PContext, n: PNode): PType =
   result = newTypeS(tyArray, c)
   rawAddSon(result, makeRangeType(c, 0, 0, n.info))
   addSonSkipIntLit(result, skipTypes(n.typ,
-      {tyGenericInst, tyVar, tyLent, tyOrdinal}), c.idgen)
+      {tyVar, tyLent, tyOrdinal}), c.idgen)
 
 proc arrayConstr(c: PContext, info: TLineInfo): PType =
   result = newTypeS(tyArray, c)
@@ -2745,6 +3007,8 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
       inc m.genericMatches
       inc m.exactMatches
     return
+  # initCandidate may have given csNoMatch if generic params didn't match:
+  if m.state == csNoMatch: return
   var marker = initIntSet()
   matchesAux(c, n, nOrig, m, marker)
   if m.state == csNoMatch: return
@@ -2767,14 +3031,16 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
           m.firstMismatch.formal = formal
           break
       else:
+        # mirrored with updateDefaultParams:
         if formal.ast.kind == nkEmpty:
           # The default param value is set to empty in `instantiateProcType`
           # when the type of the default expression doesn't match the type
           # of the instantiated proc param:
-          localError(c.config, m.call.info,
-                     ("The default parameter '$1' has incompatible type " &
-                      "with the explicitly requested proc instantiation") %
-                      formal.name.s)
+          pushInfoContext(c.config, m.call.info,
+            if m.calleeSym != nil: m.calleeSym.detailedInfo else: "")
+          typeMismatch(c.config, formal.ast.info, formal.typ, formal.ast.typ, formal.ast)
+          popInfoContext(c.config)
+          formal.ast.typ = errorType(c)
         if nfDefaultRefsParam in formal.ast.flags:
           m.call.flags.incl nfDefaultRefsParam
         var defaultValue = copyTree(formal.ast)
diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim
index d114f59da..1dd481ec0 100644
--- a/compiler/sizealignoffsetimpl.nim
+++ b/compiler/sizealignoffsetimpl.nim
@@ -385,6 +385,13 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
         accum.maxAlign = 1
         computeObjectOffsetsFoldFunction(conf, typ.n, true, accum)
       else:
+        if typ.baseClass == nil and lacksMTypeField(typ) and typ.n.len == 1 and
+            typ.n[0].kind == nkSym and
+            typ.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
+          # a dummy field is generated for an object with a single field
+          # with an UncheckedArray type
+          assert accum.offset == 0
+          accum.offset = 1
         computeObjectOffsetsFoldFunction(conf, typ.n, false, accum)
       let paddingAtEnd = int16(accum.finish())
       if typ.sym != nil and
diff --git a/compiler/spawn.nim b/compiler/spawn.nim
index 972d49d3e..58d5a4928 100644
--- a/compiler/spawn.nim
+++ b/compiler/spawn.nim
@@ -109,6 +109,16 @@ stmtList:
 
 """
 
+proc castToVoidPointer(g: ModuleGraph, n: PNode, fvField: PNode): PNode =
+  if g.config.backend == backendCpp:
+    result = fvField
+  else:
+    let ptrType = getSysType(g, n.info, tyPointer)
+    result = newNodeI(nkCast, fvField.info)
+    result.add newNodeI(nkEmpty, fvField.info)
+    result.add fvField
+    result.typ = ptrType
+
 proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
                        varSection, varInit, call, barrier, fv: PNode;
                        idgen: IdGenerator;
@@ -156,8 +166,9 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
     if barrier == nil:
       # by now 'fv' is shared and thus might have beeen overwritten! we need
       # to use the thread-local view instead:
+      let castExpr = castToVoidPointer(g, f, threadLocalProm.newSymNode)
       body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.info,
-        threadLocalProm.newSymNode)
+        castExpr)
   else:
     body.add call
   if barrier != nil:
@@ -413,7 +424,8 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp
     # create flowVar:
     result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
     if barrier == nil:
-      result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info, fvField)
+      let castExpr = castToVoidPointer(g, n, fvField)
+      result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info, castExpr)
 
   elif spawnKind == srByVar:
     var field = newSym(skField, getIdent(g.cache, "fv"), idgen, owner, n.info, g.config.options)
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 616ecd466..a5213086b 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -696,7 +696,7 @@ proc markOwnerModuleAsUsed(c: PContext; s: PSym) =
       else:
         inc i
 
-proc markUsed(c: PContext; info: TLineInfo; s: PSym) =
+proc markUsed(c: PContext; info: TLineInfo; s: PSym; checkStyle = true) =
   let conf = c.config
   incl(s.flags, sfUsed)
   if s.kind == skEnumField and s.owner != nil:
@@ -713,7 +713,8 @@ proc markUsed(c: PContext; info: TLineInfo; s: PSym) =
     if sfError in s.flags: userError(conf, info, s)
   when defined(nimsuggest):
     suggestSym(c.graph, info, s, c.graph.usageSym, false)
-  styleCheckUse(c, info, s)
+  if checkStyle:
+    styleCheckUse(c, info, s)
   markOwnerModuleAsUsed(c, s)
 
 proc safeSemExpr*(c: PContext, n: PNode): PNode =
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 070443b82..8dd24e090 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -56,6 +56,7 @@ type
     contSyms, breakSyms: seq[PSym]  # to transform 'continue' and 'break'
     deferDetected, tooEarly: bool
     isIntroducingNewLocalVars: bool  # true if we are in `introducingNewLocalVars` (don't transform yields)
+    inAddr: bool
     flags: TransformFlags
     graph: ModuleGraph
     idgen: IdGenerator
@@ -97,7 +98,7 @@ proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
   r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias, tySink})
   incl(r.flags, sfFromGeneric)
   let owner = getCurrOwner(c)
-  if owner.isIterator and not c.tooEarly:
+  if owner.isIterator and not c.tooEarly and not isDefined(c.graph.config, "nimOptIters"):
     result = freshVarForClosureIter(c.graph, r, c.idgen, owner)
   else:
     result = newSymNode(r)
@@ -176,7 +177,7 @@ proc transformSym(c: PTransf, n: PNode): PNode =
 
 proc freshVar(c: PTransf; v: PSym): PNode =
   let owner = getCurrOwner(c)
-  if owner.isIterator and not c.tooEarly:
+  if owner.isIterator and not c.tooEarly and not isDefined(c.graph.config, "nimOptIters"):
     result = freshVarForClosureIter(c.graph, v, c.idgen, owner)
   else:
     var newVar = copySym(v, c.idgen)
@@ -415,9 +416,15 @@ proc transformYield(c: PTransf, n: PNode): PNode =
       result.add transform(c, v)
 
       for i in 0..<c.transCon.forStmt.len - 2:
-        let lhs = c.transCon.forStmt[i]
-        let rhs = transform(c, newTupleAccess(c.graph, tmp, i))
-        result.add(asgnTo(lhs, rhs))
+        if c.transCon.forStmt[i].kind == nkVarTuple:
+          for j in 0..<c.transCon.forStmt[i].len-1:
+            let lhs = c.transCon.forStmt[i][j]
+            let rhs = transform(c, newTupleAccess(c.graph, newTupleAccess(c.graph, tmp, i), j))
+            result.add(asgnTo(lhs, rhs))
+        else:
+          let lhs = c.transCon.forStmt[i]
+          let rhs = transform(c, newTupleAccess(c.graph, tmp, i))
+          result.add(asgnTo(lhs, rhs))
     else:
       for i in 0..<c.transCon.forStmt.len - 2:
         let lhs = c.transCon.forStmt[i]
@@ -456,6 +463,14 @@ proc transformYield(c: PTransf, n: PNode): PNode =
       let rhs = transform(c, e)
       result.add(asgnTo(lhs, rhs))
 
+
+  # bug #23536; note that the info of forLoopBody should't change
+  for idx in 0 ..< result.len:
+    var changeNode = result[idx]
+    changeNode.info = c.transCon.forStmt.info
+    for i, child in changeNode:
+      child.info = changeNode.info
+
   inc(c.transCon.yieldStmts)
   if c.transCon.yieldStmts <= 1:
     # common case
@@ -466,12 +481,6 @@ proc transformYield(c: PTransf, n: PNode): PNode =
     result.add(introduceNewLocalVars(c, c.transCon.forLoopBody))
     c.isIntroducingNewLocalVars = false
 
-  for idx in 0 ..< result.len:
-    var changeNode = result[idx]
-    changeNode.info = c.transCon.forStmt.info
-    for i, child in changeNode:
-      child.info = changeNode.info
-
 proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode =
   result = transformSons(c, n)
   # inlining of 'var openarray' iterators; bug #19977
@@ -499,7 +508,15 @@ proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode =
       elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
         result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen)
   else:
-    if n[0].kind in kinds:
+    if n[0].kind in kinds and
+        not (n[0][0].kind == nkSym and n[0][0].sym.kind == skForVar and
+          n[0][0].typ.skipTypes(abstractVar).kind == tyTuple
+        ) and not (n[0][0].kind == nkSym and n[0][0].sym.kind == skParam and
+          n.typ.kind == tyVar and
+          n.typ.skipTypes(abstractVar).kind == tyOpenArray and
+          n[0][0].typ.skipTypes(abstractVar).kind == tyString)
+        : # elimination is harmful to `for tuple unpack` because of newTupleAccess
+          # it is also harmful to openArrayLoc (var openArray) for strings
       # addr ( deref ( x )) --> x
       result = n[0][0]
       if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
@@ -511,6 +528,7 @@ proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode =
 
   # we cannot generate a proper thunk here for GC-safety reasons
   # (see internal documentation):
+  if jsNoLambdaLifting in c.graph.config.legacyFeatures and c.graph.config.backend == backendJs: return prc
   result = newNodeIT(nkClosure, prc.info, dest)
   var conv = newNodeIT(nkHiddenSubConv, prc.info, dest)
   conv.add(newNodeI(nkEmpty, prc.info))
@@ -634,9 +652,11 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
   case arg.kind
   of nkEmpty..nkNilLit:
     result = paDirectMapping
-  of nkDotExpr, nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr:
+  of nkDotExpr, nkDerefExpr, nkHiddenDeref:
+    result = putArgInto(arg[0], formal)
+  of nkAddr, nkHiddenAddr:
     result = putArgInto(arg[0], formal)
-    #if result == paViaIndirection: result = paFastAsgn
+    if result == paViaIndirection: result = paFastAsgn
   of nkCurly, nkBracket:
     for i in 0..<arg.len:
       if putArgInto(arg[i], formal) != paDirectMapping:
@@ -1059,7 +1079,10 @@ proc transform(c: PTransf, n: PNode): PNode =
   of nkHiddenAddr:
     result = transformAddrDeref(c, n, {nkHiddenDeref})
   of nkAddr:
+    let oldInAddr = c.inAddr
+    c.inAddr = true
     result = transformAddrDeref(c, n, {nkDerefExpr, nkHiddenDeref})
+    c.inAddr = oldInAddr
   of nkDerefExpr:
     result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr})
   of nkHiddenDeref:
@@ -1139,7 +1162,7 @@ proc transform(c: PTransf, n: PNode): PNode =
   let exprIsPointerCast = n.kind in {nkCast, nkConv, nkHiddenStdConv} and
                           n.typ != nil and
                           n.typ.kind == tyPointer
-  if not exprIsPointerCast:
+  if not exprIsPointerCast and not c.inAddr:
     var cnst = getConstExpr(c.module, result, c.idgen, c.graph)
     # we inline constants if they are not complex constants:
     if cnst != nil and not dontInlineConstant(n, cnst):
diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim
index d226b2e06..39193a42d 100644
--- a/compiler/typeallowed.nim
+++ b/compiler/typeallowed.nim
@@ -27,6 +27,7 @@ type
     taProcContextIsNotMacro
     taIsCastable
     taIsDefaultField
+    taVoid # only allow direct void fields of objects/tuples
 
   TTypeAllowedFlags* = set[TTypeAllowedFlag]
 
@@ -60,6 +61,8 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
   if typ == nil: return nil
   if containsOrIncl(marker, typ.id): return nil
   var t = skipTypes(typ, abstractInst-{tyTypeDesc, tySink})
+
+  let flags = if t.kind == tyVoid: flags else: flags-{taVoid}
   case t.kind
   of tyVar, tyLent:
     if kind in {skProc, skFunc, skConst} and (views notin c.features):
@@ -115,7 +118,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
   of tyStatic:
     if kind notin {skParam}: result = t
   of tyVoid:
-    if taField notin flags: result = t
+    if taVoid notin flags: result = t
   of tyTypeClasses:
     if tfGenericTypeParam in t.flags or taConcept in flags: #or taField notin flags:
       discard
@@ -184,12 +187,12 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
         t.baseClass != nil and taIsDefaultField notin flags:
       result = t
     else:
-      let flags = flags+{taField}
+      let flags = flags+{taField, taVoid}
       result = typeAllowedAux(marker, t.baseClass, kind, c, flags)
       if result.isNil and t.n != nil:
         result = typeAllowedNode(marker, t.n, kind, c, flags)
   of tyTuple:
-    let flags = flags+{taField}
+    let flags = flags+{taField, taVoid}
     for a in t.kids:
       result = typeAllowedAux(marker, a, kind, c, flags)
       if result != nil: break
@@ -197,7 +200,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
       result = typeAllowedNode(marker, t.n, kind, c, flags)
   of tyEmpty:
     if kind in {skVar, skLet}: result = t
-  of tyProxy:
+  of tyError:
     # for now same as error node; we say it's a valid type as it should
     # prevent cascading errors:
     result = nil
diff --git a/compiler/types.nim b/compiler/types.nim
index e5ce0aea1..a441b0ea2 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -115,7 +115,7 @@ proc isPureObject*(typ: PType): bool =
 proc isUnsigned*(t: PType): bool =
   t.skipTypes(abstractInst).kind in {tyChar, tyUInt..tyUInt64}
 
-proc getOrdValue*(n: PNode; onError = high(Int128)): Int128 =
+proc getOrdValueAux*(n: PNode, err: var bool): Int128 =
   var k = n.kind
   if n.typ != nil and n.typ.skipTypes(abstractInst).kind in {tyChar, tyUInt..tyUInt64}:
     k = nkUIntLit
@@ -131,13 +131,22 @@ proc getOrdValue*(n: PNode; onError = high(Int128)): Int128 =
     toInt128(n.intVal)
   of nkNilLit:
     int128.Zero
-  of nkHiddenStdConv: getOrdValue(n[1], onError)
+  of nkHiddenStdConv:
+    getOrdValueAux(n[1], err)
   else:
-    # XXX: The idea behind the introduction of int128 was to finally
-    # have all calculations numerically far away from any
-    # overflows. This command just introduces such overflows and
-    # should therefore really be revisited.
-    onError
+    err = true
+    int128.Zero
+
+proc getOrdValue*(n: PNode): Int128 =
+  var err: bool = false
+  result = getOrdValueAux(n, err)
+  #assert err == false
+
+proc getOrdValue*(n: PNode, onError: Int128): Int128 =
+  var err = false
+  result = getOrdValueAux(n, err)
+  if err:
+    result = onError
 
 proc getFloatValue*(n: PNode): BiggestFloat =
   case n.kind
@@ -223,7 +232,11 @@ proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
   if result: return
   if not containsOrIncl(marker, t.id):
     case t.kind
-    of tyGenericInst, tyGenericBody, tyAlias, tySink, tyInferred:
+    of tyGenericBody:
+      # treat as atomic, containsUnresolvedType wants always false,
+      # containsGenericType always gives true
+      discard
+    of tyGenericInst, tyAlias, tySink, tyInferred:
       result = iterOverTypeAux(marker, skipModifier(t), iter, closure)
     else:
       for a in t.kids:
@@ -739,6 +752,16 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
       if tfThread in t.flags:
         addSep(prag)
         prag.add("gcsafe")
+      var effectsOfStr = ""
+      for i, a in t.paramTypes:
+        let j = paramTypeToNodeIndex(i)
+        if t.n != nil and j < t.n.len and t.n[j].kind == nkSym and t.n[j].sym.kind == skParam and sfEffectsDelayed in t.n[j].sym.flags:
+          addSep(effectsOfStr)
+          effectsOfStr.add(t.n[j].sym.name.s)
+      if effectsOfStr != "":
+        addSep(prag)
+        prag.add("effectsOf: ")
+        prag.add(effectsOfStr)
       if not hasImplicitRaises and prefer == preferInferredEffects and not isNil(t.owner) and not isNil(t.owner.typ) and not isNil(t.owner.typ.n) and (t.owner.typ.n.len > 0):
         let effects = t.owner.typ.n[0]
         if effects.kind == nkEffectList and effects.len == effectListLen:
@@ -767,7 +790,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
 
 proc firstOrd*(conf: ConfigRef; t: PType): Int128 =
   case t.kind
-  of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy:
+  of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyError:
     result = Zero
   of tySet, tyVar: result = firstOrd(conf, t.elementType)
   of tyArray: result = firstOrd(conf, t.indexType)
@@ -900,7 +923,7 @@ proc lastOrd*(conf: ConfigRef; t: PType): Int128 =
     result = lastOrd(conf, skipModifier(t))
   of tyUserTypeClasses:
     result = lastOrd(conf, last(t))
-  of tyProxy: result = Zero
+  of tyError: result = Zero
   of tyOrdinal:
     if t.hasElementType: result = lastOrd(conf, skipModifier(t))
     else:
@@ -975,6 +998,8 @@ type
     AllowCommonBase
     PickyCAliases  # be picky about the distinction between 'cint' and 'int32'
     IgnoreFlags    # used for borrowed functions and methods; ignores the tfVarIsPtr flag
+    PickyBackendAliases # be picky about different aliases
+    IgnoreRangeShallow
 
   TTypeCmpFlags* = set[TTypeCmpFlag]
 
@@ -1203,26 +1228,40 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
       inc c.recCheck
     else:
       if containsOrIncl(c, a, b): return true
+  template maybeSkipRange(x: set[TTypeKind]): set[TTypeKind] =
+    if IgnoreRangeShallow in c.flags:
+      x + {tyRange}
+    else:
+      x
+  
+  template withoutShallowFlags(body) =
+    let oldFlags = c.flags
+    c.flags.excl IgnoreRangeShallow
+    body
+    c.flags = oldFlags
 
   if x == y: return true
-  var a = skipTypes(x, {tyAlias})
+  let aliasSkipSet = maybeSkipRange({tyAlias})
+  var a = skipTypes(x, aliasSkipSet)
   while a.kind == tyUserTypeClass and tfResolved in a.flags:
-    a = skipTypes(a.last, {tyAlias})
-  var b = skipTypes(y, {tyAlias})
+    a = skipTypes(a.last, aliasSkipSet)
+  var b = skipTypes(y, aliasSkipSet)
   while b.kind == tyUserTypeClass and tfResolved in b.flags:
-    b = skipTypes(b.last, {tyAlias})
+    b = skipTypes(b.last, aliasSkipSet)
   assert(a != nil)
   assert(b != nil)
-  if a.kind != b.kind:
-    case c.cmp
-    of dcEq: return false
-    of dcEqIgnoreDistinct:
-      a = a.skipTypes({tyDistinct, tyGenericInst})
-      b = b.skipTypes({tyDistinct, tyGenericInst})
-      if a.kind != b.kind: return false
-    of dcEqOrDistinctOf:
-      a = a.skipTypes({tyDistinct, tyGenericInst})
-      if a.kind != b.kind: return false
+  case c.cmp
+  of dcEq:
+    if a.kind != b.kind: return false
+  of dcEqIgnoreDistinct:
+    let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst})
+    a = a.skipTypes(distinctSkipSet)
+    b = b.skipTypes(distinctSkipSet)
+    if a.kind != b.kind: return false
+  of dcEqOrDistinctOf:
+    let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst})
+    a = a.skipTypes(distinctSkipSet)
+    if a.kind != b.kind: return false
 
   #[
     The following code should not run in the case either side is an generic alias,
@@ -1230,14 +1269,16 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
     objects ie `type A[T] = SomeObject`
   ]#
   # this is required by tunique_type but makes no sense really:
-  if x.kind == tyGenericInst and IgnoreTupleFields notin c.flags and tyDistinct != y.kind:
+  if c.cmp == dcEq and x.kind == tyGenericInst and
+      IgnoreTupleFields notin c.flags and tyDistinct != y.kind:
     let
       lhs = x.skipGenericAlias
       rhs = y.skipGenericAlias
     if rhs.kind != tyGenericInst or lhs.base != rhs.base or rhs.kidsLen != lhs.kidsLen:
       return false
-    for ff, aa in underspecifiedPairs(rhs, lhs, 1, -1):
-      if not sameTypeAux(ff, aa, c): return false
+    withoutShallowFlags:
+      for ff, aa in underspecifiedPairs(rhs, lhs, 1, -1):
+        if not sameTypeAux(ff, aa, c): return false
     return true
 
   case a.kind
@@ -1251,6 +1292,11 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
       let symFlagsB = if b.sym != nil: b.sym.flags else: {}
       if (symFlagsA+symFlagsB) * {sfImportc, sfExportc} != {}:
         result = symFlagsA == symFlagsB
+    elif result and PickyBackendAliases in c.flags:
+      let symFlagsA = if a.sym != nil: a.sym.flags else: {}
+      let symFlagsB = if b.sym != nil: b.sym.flags else: {}
+      if (symFlagsA+symFlagsB) * {sfImportc, sfExportc} != {}:
+        result = a.id == b.id
 
   of tyStatic, tyFromExpr:
     result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b)
@@ -1258,9 +1304,10 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
       cycleCheck()
       result = sameTypeAux(a.skipModifier, b.skipModifier, c)
   of tyObject:
-    ifFastObjectTypeCheckFailed(a, b):
-      cycleCheck()
-      result = sameObjectStructures(a, b, c) and sameFlags(a, b)
+    withoutShallowFlags:
+      ifFastObjectTypeCheckFailed(a, b):
+        cycleCheck()
+        result = sameObjectStructures(a, b, c) and sameFlags(a, b)
   of tyDistinct:
     cycleCheck()
     if c.cmp == dcEq:
@@ -1275,8 +1322,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
   of tyError:
     result = b.kind == tyError
   of tyTuple:
-    cycleCheck()
-    result = sameTuple(a, b, c) and sameFlags(a, b)
+    withoutShallowFlags:
+      cycleCheck()
+      result = sameTuple(a, b, c) and sameFlags(a, b)
   of tyTypeDesc:
     if c.cmp == dcEqIgnoreDistinct: result = false
     elif ExactTypeDescValues in c.flags:
@@ -1300,7 +1348,8 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
      tyAnd, tyOr, tyNot, tyAnything, tyOwned:
     cycleCheck()
     if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n
-    result = sameChildrenAux(a, b, c)
+    withoutShallowFlags:
+      result = sameChildrenAux(a, b, c)
     if result and IgnoreFlags notin c.flags:
       if IgnoreTupleFields in c.flags:
         result = a.flags * {tfVarIsPtr, tfIsOutParam} == b.flags * {tfVarIsPtr, tfIsOutParam}
@@ -1313,11 +1362,21 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
                ((ExactConstraints notin c.flags) or sameConstraints(a.n, b.n))
   of tyRange:
     cycleCheck()
-    result = sameTypeOrNilAux(a.elementType, b.elementType, c) and
-        sameValue(a.n[0], b.n[0]) and
+    result = sameTypeOrNilAux(a.elementType, b.elementType, c)
+    if result and IgnoreRangeShallow notin c.flags:
+      result = sameValue(a.n[0], b.n[0]) and
         sameValue(a.n[1], b.n[1])
-  of tyGenericInst, tyAlias, tyInferred, tyIterable:
+  of tyAlias, tyInferred, tyIterable:
+    cycleCheck()
+    result = sameTypeAux(a.skipModifier, b.skipModifier, c)
+  of tyGenericInst:
+    # BUG #23445
+    # The type system must distinguish between `T[int] = object #[empty]#`
+    # and `T[float] = object #[empty]#`!
     cycleCheck()
+    withoutShallowFlags:
+      for ff, aa in underspecifiedPairs(a, b, 1, -1):
+        if not sameTypeAux(ff, aa, c): return false
     result = sameTypeAux(a.skipModifier, b.skipModifier, c)
   of tyNone: result = false
   of tyConcept:
@@ -1329,6 +1388,19 @@ proc sameBackendType*(x, y: PType): bool =
   c.cmp = dcEqIgnoreDistinct
   result = sameTypeAux(x, y, c)
 
+proc sameBackendTypeIgnoreRange*(x, y: PType): bool =
+  var c = initSameTypeClosure()
+  c.flags.incl IgnoreTupleFields
+  c.flags.incl IgnoreRangeShallow
+  c.cmp = dcEqIgnoreDistinct
+  result = sameTypeAux(x, y, c)
+
+proc sameBackendTypePickyAliases*(x, y: PType): bool =
+  var c = initSameTypeClosure()
+  c.flags.incl {IgnoreTupleFields, PickyCAliases, PickyBackendAliases}
+  c.cmp = dcEqIgnoreDistinct
+  result = sameTypeAux(x, y, c)
+
 proc compareTypes*(x, y: PType,
                    cmp: TDistinctCompare = dcEq,
                    flags: TTypeCmpFlags = {}): bool =
@@ -1389,6 +1461,8 @@ proc commonSuperclass*(a, b: PType): PType =
       return t
     y = y.baseClass
 
+proc lacksMTypeField*(typ: PType): bool {.inline.} =
+  (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags
 
 include sizealignoffsetimpl
 
@@ -1425,6 +1499,23 @@ proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
 proc containsGenericType*(t: PType): bool =
   result = iterOverType(t, containsGenericTypeIter, nil)
 
+proc containsUnresolvedTypeIter(t: PType, closure: RootRef): bool =
+  if tfUnresolved in t.flags: return true
+  case t.kind
+  of tyStatic:
+    return t.n == nil
+  of tyTypeDesc:
+    if t.base.kind == tyNone: return true
+    if containsUnresolvedTypeIter(t.base, closure): return true
+    return false
+  of tyGenericInvocation, tyGenericParam, tyFromExpr, tyAnything:
+    return true
+  else:
+    return false
+
+proc containsUnresolvedType*(t: PType): bool =
+  result = iterOverType(t, containsUnresolvedTypeIter, nil)
+
 proc baseOfDistinct*(t: PType; g: ModuleGraph; idgen: IdGenerator): PType =
   if t.kind == tyDistinct:
     result = t.elementType
@@ -1708,6 +1799,13 @@ proc processPragmaAndCallConvMismatch(msg: var string, formal, actual: PType, co
     of efTagsIllegal:
       msg.add "\n.notTag catched an illegal effect"
 
+proc typeNameAndDesc*(t: PType): string =
+  result = typeToString(t)
+  let desc = typeToString(t, preferDesc)
+  if result != desc:
+    result.add(" = ")
+    result.add(desc)
+
 proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: PNode) =
   if formal.kind != tyError and actual.kind != tyError:
     let actualStr = typeToString(actual)
@@ -1837,6 +1935,3 @@ proc isCharArrayPtr*(t: PType; allowPointerToChar: bool): bool =
       result = false
   else:
     result = false
-
-proc lacksMTypeField*(typ: PType): bool {.inline.} =
-  (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags
diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim
index 6b2f677a7..1711fea46 100644
--- a/compiler/varpartitions.nim
+++ b/compiler/varpartitions.nim
@@ -106,6 +106,7 @@ type
     unanalysableMutation: bool
     inAsgnSource, inConstructor, inNoSideEffectSection: int
     inConditional, inLoop: int
+    inConvHasDestructor: int
     owner: PSym
     g: ModuleGraph
 
@@ -427,16 +428,28 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) =
     # primitive literals including the empty are harmless:
     discard
 
-  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast, nkConv:
+  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast:
     destMightOwn(c, dest, n[1])
 
+  of nkConv:
+    if hasDestructor(n.typ):
+      inc c.inConvHasDestructor
+      destMightOwn(c, dest, n[1])
+      dec c.inConvHasDestructor
+    else:
+      destMightOwn(c, dest, n[1])
+
   of nkIfStmt, nkIfExpr:
     for i in 0..<n.len:
+      inc c.inConditional
       destMightOwn(c, dest, n[i].lastSon)
+      dec c.inConditional
 
   of nkCaseStmt:
     for i in 1..<n.len:
+      inc c.inConditional
       destMightOwn(c, dest, n[i].lastSon)
+      dec c.inConditional
 
   of nkStmtList, nkStmtListExpr:
     if n.len > 0:
@@ -481,7 +494,7 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) =
 
   of nkCallKinds:
     if n.typ != nil:
-      if hasDestructor(n.typ):
+      if hasDestructor(n.typ) or c.inConvHasDestructor > 0:
         # calls do construct, what we construct must be destroyed,
         # so dest cannot be a cursor:
         dest.flags.incl ownsData
@@ -489,8 +502,17 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) =
         # we know the result is derived from the first argument:
         var roots: seq[(PSym, int)] = @[]
         allRoots(n[1], roots, RootEscapes)
-        for r in roots:
-          connect(c, dest.sym, r[0], n[1].info)
+        if roots.len == 0 and c.inConditional > 0:
+          # when in a conditional expression,
+          # to ensure that the first argument isn't outlived
+          # by the lvalue, we need find the root, otherwise
+          # it is probably a local temporary
+          # (e.g. a return value from a call),
+          # we should prevent cursorfication
+          dest.flags.incl preventCursor
+        else:
+          for r in roots:
+            connect(c, dest.sym, r[0], n[1].info)
 
       else:
         let magic = if n[0].kind == nkSym: n[0].sym.magic else: mNone
diff --git a/compiler/vm.nim b/compiler/vm.nim
index a775cf584..161b025a6 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -507,7 +507,7 @@ proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) =
   setLen(node.sons, newLen)
   if oldLen < newLen:
     for i in oldLen..<newLen:
-      node[i] = getNullValue(typ.elementType, info, c.config)
+      node[i] = getNullValue(c, typ.elementType, info, c.config)
 
 const
   errNilAccess = "attempt to access a nil address"
@@ -609,7 +609,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcYldVal: assert false
     of opcAsgnInt:
       decodeB(rkInt)
-      regs[ra].intVal = regs[rb].intVal
+      if regs[rb].kind == rkInt:
+        regs[ra].intVal = regs[rb].intVal
+      else:
+        stackTrace(c, tos, pc, "opcAsgnInt: got " & $regs[rb].kind)
     of opcAsgnFloat:
       decodeB(rkFloat)
       regs[ra].floatVal = regs[rb].floatVal
@@ -639,6 +642,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
           regs[ra].intVal = cast[int](regs[rb].node.intVal)
         of rkNodeAddr:
           regs[ra].intVal = cast[int](regs[rb].nodeAddr)
+        of rkRegisterAddr:
+          regs[ra].intVal = cast[int](regs[rb].regAddr)
         of rkInt:
           regs[ra].intVal = regs[rb].intVal
         else:
@@ -674,16 +679,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       else:
         assert regs[rb].kind == rkNode
         let nb = regs[rb].node
-        case nb.kind
-        of nkCharLit..nkUInt64Lit:
-          ensureKind(rkInt)
-          regs[ra].intVal = nb.intVal
-        of nkFloatLit..nkFloat64Lit:
-          ensureKind(rkFloat)
-          regs[ra].floatVal = nb.floatVal
+        if nb == nil:
+          stackTrace(c, tos, pc, errNilAccess)
         else:
-          ensureKind(rkNode)
-          regs[ra].node = nb
+          case nb.kind
+          of nkCharLit..nkUInt64Lit:
+            ensureKind(rkInt)
+            regs[ra].intVal = nb.intVal
+          of nkFloatLit..nkFloat64Lit:
+            ensureKind(rkFloat)
+            regs[ra].floatVal = nb.floatVal
+          else:
+            ensureKind(rkNode)
+            regs[ra].node = nb
     of opcSlice:
       # A bodge, but this takes in `toOpenArray(rb, rc, rc)` and emits
       # nkTupleConstr(x, y, z) into the `regs[ra]`. These can later be used for calculating the slice we have taken.
@@ -848,25 +856,30 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcLdObj:
       # a = b.c
       decodeBC(rkNode)
-      let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
-      case src.kind
-      of nkEmpty..nkNilLit:
-        # for nkPtrLit, this could be supported in the future, use something like:
-        # derefPtrToReg(src.intVal + offsetof(src.typ, rc), typ_field, regs[ra], isAssign = false)
-        # where we compute the offset in bytes for field rc
-        stackTrace(c, tos, pc, errNilAccess & " " & $("kind", src.kind, "typ", typeToString(src.typ), "rc", rc))
-      of nkObjConstr:
-        let n = src[rc + 1].skipColon
-        regs[ra].node = n
-      of nkTupleConstr:
-        let n = if src.typ != nil and tfTriggersCompileTime in src.typ.flags:
-            src[rc]
-          else:
-            src[rc].skipColon
-        regs[ra].node = n
+      if rb >= regs.len or regs[rb].kind == rkNone or 
+        (regs[rb].kind == rkNode and regs[rb].node == nil) or
+        (regs[rb].kind == rkNodeAddr and regs[rb].nodeAddr[] == nil): 
+        stackTrace(c, tos, pc, errNilAccess)
       else:
-        let n = src[rc]
-        regs[ra].node = n
+        let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
+        case src.kind
+        of nkEmpty..nkNilLit:
+          # for nkPtrLit, this could be supported in the future, use something like:
+          # derefPtrToReg(src.intVal + offsetof(src.typ, rc), typ_field, regs[ra], isAssign = false)
+          # where we compute the offset in bytes for field rc
+          stackTrace(c, tos, pc, errNilAccess & " " & $("kind", src.kind, "typ", typeToString(src.typ), "rc", rc))
+        of nkObjConstr:
+          let n = src[rc + 1].skipColon
+          regs[ra].node = n
+        of nkTupleConstr:
+          let n = if src.typ != nil and tfTriggersCompileTime in src.typ.flags:
+              src[rc]
+            else:
+              src[rc].skipColon
+          regs[ra].node = n
+        else:
+          let n = src[rc]
+          regs[ra].node = n
     of opcLdObjAddr:
       # a = addr(b.c)
       decodeBC(rkNodeAddr)
@@ -892,8 +905,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         stackTrace(c, tos, pc, errNilAccess)
       elif dest[shiftedRb].kind == nkExprColonExpr:
         writeField(dest[shiftedRb][1], regs[rc])
+        dest[shiftedRb][1].flags.incl nfSkipFieldChecking
       else:
         writeField(dest[shiftedRb], regs[rc])
+        dest[shiftedRb].flags.incl nfSkipFieldChecking
     of opcWrStrIdx:
       decodeBC(rkNode)
       let idx = regs[rb].intVal.int
@@ -1361,7 +1376,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         else:
           internalError(c.config, c.debug[pc], "opcParseFloat: Incorrectly created openarray")
       else:
-        regs[ra].intVal = parseBiggestFloat(regs[ra].node.strVal, rcAddr.floatVal)
+        regs[ra].intVal = parseBiggestFloat(regs[rb].node.strVal, rcAddr.floatVal)
 
     of opcRangeChck:
       let rb = instr.regB
@@ -1376,7 +1391,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       let rb = instr.regB
       let rc = instr.regC
       let bb = regs[rb].node
+      if bb.kind == nkNilLit:
+        stackTrace(c, tos, pc, "attempt to call nil closure")
       let isClosure = bb.kind == nkTupleConstr
+      if isClosure and bb[0].kind == nkNilLit:
+        stackTrace(c, tos, pc, "attempt to call nil closure")
       let prc = if not isClosure: bb.sym else: bb[0].sym
       if prc.offset < -1:
         # it's a callback:
@@ -1416,7 +1435,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos)
         newSeq(newFrame.slots, prc.offset+ord(isClosure))
         if not isEmptyType(prc.typ.returnType):
-          putIntoReg(newFrame.slots[0], getNullValue(prc.typ.returnType, prc.info, c.config))
+          putIntoReg(newFrame.slots[0], getNullValue(c, prc.typ.returnType, prc.info, c.config))
         for i in 1..rc-1:
           newFrame.slots[i] = regs[rb+i]
         if isClosure:
@@ -1549,7 +1568,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNew:
       ensureKind(rkNode)
       let typ = c.types[instr.regBx - wordExcess]
-      regs[ra].node = getNullValue(typ, c.debug[pc], c.config)
+      regs[ra].node = getNullValue(c, typ, c.debug[pc], c.config)
       regs[ra].node.flags.incl nfIsRef
     of opcNewSeq:
       let typ = c.types[instr.regBx - wordExcess]
@@ -1561,7 +1580,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       regs[ra].node.typ = typ
       newSeq(regs[ra].node.sons, count)
       for i in 0..<count:
-        regs[ra].node[i] = getNullValue(typ.elementType, c.debug[pc], c.config)
+        regs[ra].node[i] = getNullValue(c, typ.elementType, c.debug[pc], c.config)
     of opcNewStr:
       decodeB(rkNode)
       regs[ra].node = newNodeI(nkStrLit, c.debug[pc])
@@ -1573,7 +1592,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcLdNull:
       ensureKind(rkNode)
       let typ = c.types[instr.regBx - wordExcess]
-      regs[ra].node = getNullValue(typ, c.debug[pc], c.config)
+      regs[ra].node = getNullValue(c, typ, c.debug[pc], c.config)
       # opcLdNull really is the gist of the VM's problems: should it load
       # a fresh null to  regs[ra].node  or to regs[ra].node[]? This really
       # depends on whether regs[ra] represents the variable itself or whether
@@ -2302,7 +2321,7 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
 
       # setup parameters:
       if not isEmptyType(sym.typ.returnType) or sym.kind == skMacro:
-        putIntoReg(tos.slots[0], getNullValue(sym.typ.returnType, sym.info, c.config))
+        putIntoReg(tos.slots[0], getNullValue(c, sym.typ.returnType, sym.info, c.config))
       # XXX We could perform some type checking here.
       for i in 0..<sym.typ.paramsLen:
         putIntoReg(tos.slots[i+1], args[i])
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index e293ab7b2..294aaaa79 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -237,7 +237,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
   of tySequence: result = mapTypeToBracket("seq", mSeq, t, info)
   of tyProc:
     if inst:
-      result = newNodeX(nkProcTy)
+      result = newNodeX(if tfIterator in t.flags: nkIteratorTy else: nkProcTy)
       var fp = newNodeX(nkFormalParams)
       if t.returnType == nil:
         fp.add newNodeI(nkEmpty, info)
@@ -246,8 +246,15 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
       for i in FirstParamAt..<t.kidsLen:
         fp.add newIdentDefs(t.n[i], t[i])
       result.add fp
-      result.add if t.n[0].len > 0: t.n[0][pragmasEffects].copyTree
-                 else: newNodeI(nkEmpty, info)
+      var prag =
+        if t.n[0].len > 0:
+          t.n[0][pragmasEffects].copyTree
+        else:
+          newNodeI(nkEmpty, info)
+      if t.callConv != ccClosure or tfExplicitCallConv in t.flags:
+        if prag.kind == nkEmpty: prag = newNodeI(nkPragma, info)
+        prag.add newIdentNode(getIdent(cache, $t.callConv), info)
+      result.add prag
     else:
       result = mapTypeToBracket("proc", mNone, t, info)
   of tyOpenArray: result = mapTypeToBracket("openArray", mOpenArray, t, info)
@@ -282,7 +289,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
   of tyUInt32: result = atomicType("uint32", mUInt32)
   of tyUInt64: result = atomicType("uint64", mUInt64)
   of tyVarargs: result = mapTypeToBracket("varargs", mVarargs, t, info)
-  of tyProxy: result = atomicType("error", mNone)
+  of tyError: result = atomicType("error", mNone)
   of tyBuiltInTypeClass:
     result = mapTypeToBracket("builtinTypeClass", mNone, t, info)
   of tyUserTypeClass, tyUserTypeClassInst:
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 366fc7b29..0c7a49984 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -53,6 +53,7 @@ type
     gfNode # Affects how variables are loaded - always loads as rkNode
     gfNodeAddr # Affects how variables are loaded - always loads as rkNodeAddr
     gfIsParam # do not deepcopy parameters, they are immutable
+    gfIsSinkParam # deepcopy sink parameters
   TGenFlags = set[TGenFlag]
 
 proc debugInfo(c: PCtx; info: TLineInfo): string =
@@ -245,7 +246,7 @@ proc getTemp(cc: PCtx; tt: PType): TRegister =
 
 proc freeTemp(c: PCtx; r: TRegister) =
   let c = c.prc
-  if c.regInfo[r].kind in {slotSomeTemp..slotTempComplex}:
+  if r < c.regInfo.len and c.regInfo[r].kind in {slotSomeTemp..slotTempComplex}:
     # this seems to cause https://github.com/nim-lang/Nim/issues/10647
     c.regInfo[r].inUse = false
 
@@ -357,12 +358,13 @@ proc genBlock(c: PCtx; n: PNode; dest: var TDest) =
     #if c.prc.regInfo[i].kind in {slotFixedVar, slotFixedLet}:
     if i != dest:
       when not defined(release):
-        if c.prc.regInfo[i].inUse and c.prc.regInfo[i].kind in {slotTempUnknown,
-                                  slotTempInt,
-                                  slotTempFloat,
-                                  slotTempStr,
-                                  slotTempComplex}:
-          raiseAssert "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind
+        if c.config.cmd != cmdCheck:
+          if c.prc.regInfo[i].inUse and c.prc.regInfo[i].kind in {slotTempUnknown,
+                                    slotTempInt,
+                                    slotTempFloat,
+                                    slotTempStr,
+                                    slotTempComplex}:
+            raiseAssert "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind
       c.prc.regInfo[i] = (inUse: false, kind: slotEmpty)
 
   c.clearDest(n, dest)
@@ -619,10 +621,17 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) =
   let fntyp = skipTypes(n[0].typ, abstractInst)
   for i in 0..<n.len:
     var r: TRegister = x+i
-    c.gen(n[i], r, {gfIsParam})
     if i >= fntyp.signatureLen:
+      c.gen(n[i], r, {gfIsParam})
       internalAssert c.config, tfVarargs in fntyp.flags
       c.gABx(n, opcSetType, r, c.genType(n[i].typ))
+    else:
+      if fntyp[i] != nil and fntyp[i].kind == tySink and
+          fntyp[i].skipTypes({tySink}).kind in {tyObject, tyString, tySequence}:
+        c.gen(n[i], r, {gfIsSinkParam})
+      else:
+        c.gen(n[i], r, {gfIsParam})
+
   if dest < 0:
     c.gABC(n, opcIndCall, 0, x, n.len)
   else:
@@ -696,6 +705,9 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
       let dest = c.genx(le, {gfNodeAddr})
       c.gABC(le, opcWrDeref, dest, 0, value)
       c.freeTemp(dest)
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+    if sameBackendType(le.typ, le[1].typ):
+      genAsgnPatch(c, le[1], value)
   else:
     discard
 
@@ -868,7 +880,7 @@ proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
     genBinaryABC(c, n, dest, opc)
   c.genNarrow(n, dest)
 
-proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
+proc genConv(c: PCtx; n, arg: PNode; dest: var TDest, flags: TGenFlags = {}; opc=opcConv) =
   let t2 = n.typ.skipTypes({tyDistinct})
   let targ2 = arg.typ.skipTypes({tyDistinct})
 
@@ -882,7 +894,7 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
       result = false
 
   if implicitConv():
-    gen(c, arg, dest)
+    gen(c, arg, dest, flags)
     return
 
   let tmp = c.genx(arg)
@@ -900,9 +912,15 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) =
   c.freeTemp(tmp)
 
 proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) =
+  template isSigned(typ: PType): bool {.dirty.} =
+    typ.kind == tyEnum and firstOrd(c.config, typ) < 0 or
+    typ.kind in {tyInt..tyInt64}
+  template isUnsigned(typ: PType): bool {.dirty.} =
+    typ.kind == tyEnum and firstOrd(c.config, typ) >= 0 or
+    typ.kind in {tyUInt..tyUInt64, tyChar, tyBool}
+
   const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar, tyEnum, tyBool}
-  var signedIntegers = {tyInt..tyInt64}
-  var unsignedIntegers = {tyUInt..tyUInt64, tyChar, tyEnum, tyBool}
+
   let src = n[1].typ.skipTypes(abstractRange)#.kind
   let dst = n[0].typ.skipTypes(abstractRange)#.kind
   let srcSize = getSize(c.config, src)
@@ -914,12 +932,12 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) =
     if dest < 0: dest = c.getTemp(n[0].typ)
     c.gABC(n, opcAsgnInt, dest, tmp)
     if dstSize != sizeof(BiggestInt): # don't do anything on biggest int types
-      if dst.kind in signedIntegers: # we need to do sign extensions
+      if isSigned(dst): # we need to do sign extensions
         if dstSize <= srcSize:
           # Sign extension can be omitted when the size increases.
           c.gABC(n, opcSignExtend, dest, TRegister(dstSize*8))
-      elif dst.kind in unsignedIntegers:
-        if src.kind in signedIntegers or dstSize < srcSize:
+      elif isUnsigned(dst):
+        if isSigned(src) or dstSize < srcSize:
           # Cast from signed to unsigned always needs narrowing. Cast
           # from unsigned to unsigned only needs narrowing when target
           # is smaller than source.
@@ -947,7 +965,7 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) =
     if dest < 0: dest = c.getTemp(n[0].typ)
     if src.kind == tyFloat32:
       c.gABC(n, opcCastFloatToInt32, dest, tmp)
-      if dst.kind in unsignedIntegers:
+      if isUnsigned(dst):
         # integers are sign extended by default.
         # since there is no opcCastFloatToUInt32, narrowing should do the trick.
         c.gABC(n, opcNarrowU, dest, TRegister(32))
@@ -1044,7 +1062,7 @@ proc whichAsgnOpc(n: PNode; requiresCopy = true): TOpcode =
   else:
     (if requiresCopy: opcAsgnComplex else: opcFastAsgnComplex)
 
-proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
+proc genMagic(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}, m: TMagic) =
   case m
   of mAnd: c.genAndOr(n, opcFJmp, dest)
   of mOr:  c.genAndOr(n, opcTJmp, dest)
@@ -1183,7 +1201,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8):
       c.gABC(n, opcNarrowU, dest, TRegister(size*8))
   of mCharToStr, mBoolToStr, mCStrToStr, mStrToStr, mEnumToStr:
-    genConv(c, n, n[1], dest)
+    genConv(c, n, n[1], dest, flags)
   of mEqStr: genBinaryABC(c, n, dest, opcEqStr)
   of mEqCString: genBinaryABC(c, n, dest, opcEqCString)
   of mLeStr: genBinaryABC(c, n, dest, opcLeStr)
@@ -1229,13 +1247,6 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.freeTemp(tmp1)
     c.genAsgnPatch(d2AsNode, d2)
     c.freeTemp(d2)
-  of mReset:
-    unused(c, n, dest)
-    var d = c.genx(n[1])
-    # XXX use ldNullOpcode() here?
-    c.gABx(n, opcLdNull, d, c.genType(n[1].typ))
-    c.gABC(n, opcNodeToReg, d, d)
-    c.genAsgnPatch(n[1], d)
   of mDefault, mZeroDefault:
     if dest < 0: dest = c.getTemp(n.typ)
     c.gABx(n, ldNullOpcode(n.typ), dest, c.genType(n.typ))
@@ -1530,7 +1541,11 @@ proc setSlot(c: PCtx; v: PSym) =
   if v.position == 0:
     v.position = getFreeRegister(c, if v.kind == skLet: slotFixedLet else: slotFixedVar, start = 1)
 
-proc cannotEval(c: PCtx; n: PNode) {.noinline.} =
+template cannotEval(c: PCtx; n: PNode) =
+  if c.config.cmd == cmdCheck:
+    localError(c.config, n.info, "cannot evaluate at compile time: " & 
+    n.renderTree)
+    return
   globalError(c.config, n.info, "cannot evaluate at compile time: " &
     n.renderTree)
 
@@ -1653,6 +1668,9 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
         c.freeTemp(cc)
       else:
         gen(c, ri, dest)
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+    if sameBackendType(le.typ, le[1].typ):
+      genAsgn(c, le[1], ri, requiresCopy)
   else:
     let dest = c.genx(le, {gfNodeAddr})
     genAsgn(c, dest, ri, requiresCopy)
@@ -1691,10 +1709,10 @@ proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
     localError(c.config, info,
                "cannot 'importc' variable at compile time; " & s.name.s)
 
-proc getNullValue*(typ: PType, info: TLineInfo; conf: ConfigRef): PNode
+proc getNullValue*(c: PCtx; typ: PType, info: TLineInfo; conf: ConfigRef): PNode
 
 proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
-  c.globals.add(getNullValue(s.typ, n.info, c.config))
+  c.globals.add(getNullValue(c, s.typ, n.info, c.config))
   s.position = c.globals.len
   # This is rather hard to support, due to the laziness of the VM code
   # generator. See tests/compile/tmacro2 for why this is necessary:
@@ -1730,6 +1748,8 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
         c.gABx(n, opcLdGlobalAddr, dest, s.position)
     elif isImportcVar:
       c.gABx(n, opcLdGlobalDerefFFI, dest, s.position)
+    elif gfIsSinkParam in flags:
+      genAsgn(c, dest, n, requiresCopy = true)
     elif fitsRegister(s.typ) and gfNode notin flags:
       var cc = c.getTemp(n.typ)
       c.gABx(n, opcLdGlobal, cc, s.position)
@@ -1743,7 +1763,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
                           s.kind in {skParam, skResult}):
       if dest < 0:
         dest = s.position + ord(s.kind == skParam)
-        internalAssert(c.config, c.prc.regInfo[dest].kind < slotSomeTemp)
+        internalAssert(c.config, c.prc.regInfo.len > dest and c.prc.regInfo[dest].kind < slotSomeTemp)
       else:
         # we need to generate an assignment:
         let requiresCopy = c.prc.regInfo[dest].kind >= slotSomeTemp and
@@ -1873,21 +1893,21 @@ proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
     let opc = if gfNodeAddr in flags: opcLdArrAddr else: opcLdArr
     genArrAccessOpcode(c, n, dest, opc, flags)
 
-proc getNullValueAux(t: PType; obj: PNode, result: PNode; conf: ConfigRef; currPosition: var int) =
+proc getNullValueAux(c: PCtx; t: PType; obj: PNode, result: PNode; conf: ConfigRef; currPosition: var int) =
   if t != nil and t.baseClass != nil:
     let b = skipTypes(t.baseClass, skipPtrs)
-    getNullValueAux(b, b.n, result, conf, currPosition)
+    getNullValueAux(c, b, b.n, result, conf, currPosition)
   case obj.kind
   of nkRecList:
-    for i in 0..<obj.len: getNullValueAux(nil, obj[i], result, conf, currPosition)
+    for i in 0..<obj.len: getNullValueAux(c, nil, obj[i], result, conf, currPosition)
   of nkRecCase:
-    getNullValueAux(nil, obj[0], result, conf, currPosition)
+    getNullValueAux(c, nil, obj[0], result, conf, currPosition)
     for i in 1..<obj.len:
-      getNullValueAux(nil, lastSon(obj[i]), result, conf, currPosition)
+      getNullValueAux(c, nil, lastSon(obj[i]), result, conf, currPosition)
   of nkSym:
     let field = newNodeI(nkExprColonExpr, result.info)
     field.add(obj)
-    let value = getNullValue(obj.sym.typ, result.info, conf)
+    let value = getNullValue(c, obj.sym.typ, result.info, conf)
     value.flags.incl nfSkipFieldChecking
     field.add(value)
     result.add field
@@ -1895,7 +1915,7 @@ proc getNullValueAux(t: PType; obj: PNode, result: PNode; conf: ConfigRef; currP
     inc currPosition
   else: globalError(conf, result.info, "cannot create null element for: " & $obj)
 
-proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
+proc getNullValue(c: PCtx; typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
   var t = skipTypes(typ, abstractRange+{tyStatic, tyOwned}-{tyTypeDesc})
   case t.kind
   of tyBool, tyEnum, tyChar, tyInt..tyInt64:
@@ -1915,22 +1935,22 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
       result = newNodeIT(nkNilLit, info, t)
     else:
       result = newNodeIT(nkTupleConstr, info, t)
-      result.add(newNodeIT(nkNilLit, info, t))
-      result.add(newNodeIT(nkNilLit, info, t))
+      result.add(newNodeIT(nkNilLit, info, getSysType(c.graph, info, tyPointer)))
+      result.add(newNodeIT(nkNilLit, info, getSysType(c.graph, info, tyPointer)))
   of tyObject:
     result = newNodeIT(nkObjConstr, info, t)
     result.add(newNodeIT(nkEmpty, info, t))
     # initialize inherited fields, and all in the correct order:
     var currPosition = 0
-    getNullValueAux(t, t.n, result, conf, currPosition)
+    getNullValueAux(c, t, t.n, result, conf, currPosition)
   of tyArray:
     result = newNodeIT(nkBracket, info, t)
     for i in 0..<toInt(lengthOrd(conf, t)):
-      result.add getNullValue(elemType(t), info, conf)
+      result.add getNullValue(c, elemType(t), info, conf)
   of tyTuple:
     result = newNodeIT(nkTupleConstr, info, t)
     for a in t.kids:
-      result.add getNullValue(a, info, conf)
+      result.add getNullValue(c, a, info, conf)
   of tySet:
     result = newNodeIT(nkCurly, info, t)
   of tySequence, tyOpenArray:
@@ -1958,7 +1978,7 @@ proc genVarSection(c: PCtx; n: PNode) =
         if s.position == 0:
           if importcCond(c, s): c.importcSym(a.info, s)
           else:
-            let sa = getNullValue(s.typ, a.info, c.config)
+            let sa = getNullValue(c, s.typ, a.info, c.config)
             #if s.ast.isNil: getNullValue(s.typ, a.info)
             #else: s.ast
             assert sa.kind != nkCall
@@ -1980,7 +2000,7 @@ proc genVarSection(c: PCtx; n: PNode) =
           # the problem is that closure types are tuples in VM, but the types of its children
           # shouldn't have the same type as closure types.
           let tmp = c.genx(a[0], {gfNodeAddr})
-          let sa = getNullValue(s.typ, a.info, c.config)
+          let sa = getNullValue(c, s.typ, a.info, c.config)
           let val = c.genx(sa)
           c.genAdditionalCopy(sa, opcWrDeref, tmp, 0, val)
           c.freeTemp(val)
@@ -2165,7 +2185,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
     if n[0].kind == nkSym:
       let s = n[0].sym
       if s.magic != mNone:
-        genMagic(c, n, dest, s.magic)
+        genMagic(c, n, dest, flags, s.magic)
       elif s.kind == skMethod:
         localError(c.config, n.info, "cannot call method " & s.name.s &
           " at compile time")
@@ -2183,7 +2203,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
       genLit(c, n, dest)
   of nkUIntLit..pred(nkNilLit): genLit(c, n, dest)
   of nkNilLit:
-    if not n.typ.isEmptyType: genLit(c, getNullValue(n.typ, n.info, c.config), dest)
+    if not n.typ.isEmptyType: genLit(c, getNullValue(c, n.typ, n.info, c.config), dest)
     else: unused(c, n, dest)
   of nkAsgn, nkFastAsgn, nkSinkAsgn:
     unused(c, n, dest)
@@ -2222,11 +2242,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
     unused(c, n, dest)
     gen(c, n[0])
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
-    genConv(c, n, n[1], dest)
+    genConv(c, n, n[1], dest, flags)
   of nkObjDownConv:
-    genConv(c, n, n[0], dest)
+    genConv(c, n, n[0], dest, flags)
   of nkObjUpConv:
-    genConv(c, n, n[0], dest)
+    genConv(c, n, n[0], dest, flags)
   of nkVarSection, nkLetSection:
     unused(c, n, dest)
     genVarSection(c, n)
@@ -2235,18 +2255,21 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
     #discard genProc(c, s)
     genLit(c, newSymNode(n[namePos].sym), dest)
   of nkChckRangeF, nkChckRange64, nkChckRange:
-    let
-      tmp0 = c.genx(n[0])
-      tmp1 = c.genx(n[1])
-      tmp2 = c.genx(n[2])
-    c.gABC(n, opcRangeChck, tmp0, tmp1, tmp2)
-    c.freeTemp(tmp1)
-    c.freeTemp(tmp2)
-    if dest >= 0:
-      gABC(c, n, whichAsgnOpc(n), dest, tmp0)
-      c.freeTemp(tmp0)
+    if skipTypes(n.typ, abstractVar).kind in {tyUInt..tyUInt64}:
+      genConv(c, n, n[0], dest, flags)
     else:
-      dest = tmp0
+      let
+        tmp0 = c.genx(n[0])
+        tmp1 = c.genx(n[1])
+        tmp2 = c.genx(n[2])
+      c.gABC(n, opcRangeChck, tmp0, tmp1, tmp2)
+      c.freeTemp(tmp1)
+      c.freeTemp(tmp2)
+      if dest >= 0:
+        gABC(c, n, whichAsgnOpc(n), dest, tmp0)
+        c.freeTemp(tmp0)
+      else:
+        dest = tmp0
   of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma,
      nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt,
      nkMixinStmt, nkBindStmt, declarativeDefs, nkMacroDef:
@@ -2259,7 +2282,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
   of nkPar, nkClosure, nkTupleConstr: genTupleConstr(c, n, dest)
   of nkCast:
     if allowCast in c.features:
-      genConv(c, n, n[1], dest, opcCast)
+      genConv(c, n, n[1], dest, flags, opcCast)
     else:
       genCastIntFloat(c, n, dest)
   of nkTypeOfExpr:
diff --git a/compiler/vmops.nim b/compiler/vmops.nim
index 23b41fd2e..45194e633 100644
--- a/compiler/vmops.nim
+++ b/compiler/vmops.nim
@@ -27,6 +27,7 @@ from std/envvars import getEnv, existsEnv, delEnv, putEnv, envPairs
 from std/os import getAppFilename
 from std/private/oscommon import dirExists, fileExists
 from std/private/osdirs import walkDir, createDir
+from std/private/ospaths2 import getCurrentDir
 
 from std/times import cpuTime
 from std/hashes import hash
@@ -341,8 +342,8 @@ proc registerAdditionalOps*(c: PCtx) =
     ## reproducible builds and users need to understand that this runs at CT.
     ## Note that `staticExec` can already do equal amount of damage so it's more
     ## of a semantic issue than a security issue.
-    registerCallback c, "stdlib.os.getCurrentDir", proc (a: VmArgs) {.nimcall.} =
-      setResult(a, os.getCurrentDir())
+    registerCallback c, "stdlib.ospaths2.getCurrentDir", proc (a: VmArgs) {.nimcall.} =
+      setResult(a, getCurrentDir())
     registerCallback c, "stdlib.osproc.execCmdEx", proc (a: VmArgs) {.nimcall.} =
       let options = getNode(a, 1).fromLit(set[osproc.ProcessOption])
       a.setResult osproc.execCmdEx(getString(a, 0), options).toLit