# # # The Nim Compiler # (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # included from cgen.nim const RangeExpandLimit = 256 # do not generate ranges # over 'RangeExpandLimit' elements stringCaseThreshold = 8 # above X strings a hash-switch for strings is generated proc getTraverseProc(p: BProc, v: PSym): Rope = if p.config.selectedGC in {gcMarkAndSweep, gcHooks, gcV2, gcRefc} and optOwnedRefs notin p.config.globalOptions and containsGarbageCollectedRef(v.loc.t): # we register a specialized marked proc here; this has the advantage # that it works out of the box for thread local storage then :-) result = genTraverseProcForGlobal(p.module, v, v.info) proc registerTraverseProc(p: BProc, v: PSym, traverseProc: Rope) = if sfThread in v.flags: appcg(p.module, p.module.preInitProc.procSec(cpsInit), "$n\t#nimRegisterThreadLocalMarker($1);$n$n", [traverseProc]) else: appcg(p.module, p.module.preInitProc.procSec(cpsInit), "$n\t#nimRegisterGlobalMarker($1);$n$n", [traverseProc]) proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} = if n.kind == nkEmpty: return false if isInvalidReturnType(conf, n.typ): # var v = f() # is transformed into: var v; f(addr v) # where 'f' **does not** initialize the result! return false result = true proc inExceptBlockLen(p: BProc): int = for x in p.nestedTryStmts: if x.inExcept: result.inc proc startBlockInternal(p: BProc): int {.discardable.} = inc(p.labels) result = p.blocks.len setLen(p.blocks, result + 1) p.blocks[result].id = p.labels p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16 p.blocks[result].nestedExceptStmts = p.inExceptBlockLen.int16 template startBlock(p: BProc, start: FormatStr = "{$n", args: varargs[Rope]): int = lineCg(p, cpsStmts, start, args) startBlockInternal(p) proc endBlock(p: BProc) proc genVarTuple(p: BProc, n: PNode) = var tup, field: TLoc if n.kind != nkVarTuple: internalError(p.config, n.info, "genVarTuple") # if we have a something that's been captured, use the lowering instead: for i in 0.. 2 # do not close and reopen blocks if this is a 'global' but inside of a block (if/while/block) forHcr = forHcr and not isGlobalInBlock if forHcr: # check with the boolean if the initializing code for the tuple should be ran lineCg(p, cpsStmts, "if ($1)$n", [hcrCond]) startBlock(p) genLineDir(p, n) initLocExpr(p, n[^1], tup) var t = tup.t.skipTypes(abstractInst) for i in 0.. 0: result.addf("FR_.len+=$1;$n", [b.frameLen.rope]) result.add(b.sections[cpsInit]) result.add(b.sections[cpsStmts]) proc endBlock(p: BProc, blockEnd: Rope) = let topBlock = p.blocks.len-1 # the block is merged into the parent block p.blocks[topBlock-1].sections[cpsStmts].add(p.blocks[topBlock].blockBody) setLen(p.blocks, topBlock) # this is done after the block is popped so $n is # properly indented when pretty printing is enabled line(p, cpsStmts, blockEnd) proc endBlock(p: BProc) = let topBlock = p.blocks.len - 1 let frameLen = p.blocks[topBlock].frameLen var blockEnd: Rope if frameLen > 0: blockEnd.addf("FR_.len-=$1;$n", [frameLen.rope]) if p.blocks[topBlock].label != nil: blockEnd.addf("} $1: ;$n", [p.blocks[topBlock].label]) else: blockEnd.addf("}$n", []) endBlock(p, blockEnd) proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} = startBlock(p) genStmts(p, stmts
discard """
  output: '''4
0
4
4
1
2
3
yes int
string int
true'''
  joinable: false
"""

import hashes

type
  Comparable = concept # no T, an atom
    proc cmp(a, b: Self): int

  ToStringable = concept
    proc `$`(a: Self): string

  Hashable = concept   ## the most basic of identity assumptions
    proc hash(x: Self): int
    proc `==`(x, y: Self): bool

  Swapable = concept
    proc swap(x, y: var Self)


proc h(x: Hashable) =
  echo x

h(4)

when true:
  proc compare(a: Comparable) =
    echo cmp(a, a)

  compare(4)

proc dollar(x: ToStringable) =
  echo x

when true:
  dollar 4
  dollar "4"

#type D = distinct int

#dollar D(4)

when true:
  type
    Iterable[Ix] = concept
      iterator items(c: Self): Ix

  proc g[Tu](it: Iterable[Tu]) =
    for x in it:
      echo x

  g(@[1, 2, 3])

proc hs(x: Swapable) =
  var y = x
  swap y, y

hs(4)

type
  Indexable[T] = concept # has a T, a collection
    proc `[]`(a: Self; index: int): T # we need to describe how to infer 'T'
    # and then we can use the 'T' and it must match:
    proc `[]=`(a: var Self; index: int; value: T)
    proc len(a: Self): int

proc indexOf[T](a: Indexable[T]; value: T) =
  echo "yes ", T

block:
  var x = @[1, 2, 3]
  indexOf(x, 4)

import tables, typetraits

type
  Dict[K, V] = concept
    proc `[]`(s: Self; k: K): V
    proc `[]=`(s: var Self; k: K; v: V)

proc d[K2, V2](x: Dict[K2, V2]) =
  echo K2, " ", V2

var x = initTable[string, int]()
d(x)


type Monoid = concept
  proc `+`(x, y: Self): Self
  proc z(t: typedesc[Self]): Self

proc z(x: typedesc[int]): int = 0

echo(int is Monoid)
if b[i].kind == nkRange: initLocExpr(p, b[i][0], x) initLocExpr(p, b[i][1], y) lineCg(p, cpsStmts, rangeFormat, [rdCharLoc(e), rdCharLoc(x), rdCharLoc(y), labl]) else: initLocExpr(p, b[i], x) lineCg(p, cpsStmts, eqFormat, [rdCharLoc(e), rdCharLoc(x), labl]) proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc, labId, until: int): TLabel = var lend = getLabel(p) for i in 1..until: # bug #4230: avoid false sharing between branches: if d.k == locTemp and isEmptyType(t.typ): d.k = locNone lineF(p, cpsStmts, "LA$1_: ;$n", [rope(labId + i)]) if t[i].kind == nkOfBranch: exprBlock(p, t[i][^1], d) lineF(p, cpsStmts, "goto $1;$n", [lend]) else: exprBlock(p, t[i][0], d) result = lend template genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc, rangeFormat, eqFormat: FormatStr, until: int, a: TLoc): TLabel = # generate a C-if statement for a Nim case statement var res: TLabel var labId = p.labels for i in 1..until: inc(p.labels) if t[i].kind == nkOfBranch: # else statement genCaseGenericBranch(p, t[i], a, rangeFormat, eqFormat, "LA" & rope(p.labels) & "_") else: lineF(p, cpsStmts, "goto LA$1_;$n", [rope(p.labels)]) if until < t.len-1: inc(p.labels) var gotoTarget = p.labels lineF(p, cpsStmts, "goto LA$1_;$n", [rope(gotoTarget)]) res = genCaseSecondPass(p, t, d, labId, until) lineF(p, cpsStmts, "LA$1_: ;$n", [rope(gotoTarget)]) else: res = genCaseSecondPass(p, t, d, labId, until) res template genCaseGeneric(p: BProc, t: PNode, d: var TLoc, rangeFormat, eqFormat: FormatStr) = var a: TLoc initLocExpr(p, t[0], a) var lend = genIfForCaseUntil(p, t, d, rangeFormat, eqFormat, t.len-1, a) fixLabel(p, lend) proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel, branches: var openArray[Rope]) = var x: TLoc for i in 0.. stringCaseThreshold: var bitMask = math.nextPowerOfTwo(strings) - 1 var branches: seq[Rope] newSeq(branches, bitMask + 1) var a: TLoc initLocExpr(p, t[0], a) # fist pass: generate ifs+goto: var labId = p.labels for i in 1.. RangeExpandLimit: return true proc ifSwitchSplitPoint(p: BProc, n: PNode): int = for i in 1.. 0: genIfForCaseUntil(p, n, d, rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n", eqFormat = "if ($1 == $2) goto $3;$n", splitPoint, a) else: nil # generate switch part (might be empty): if splitPoint+1 < n.len: lineF(p, cpsStmts, "switch ($1) {$n", [rdCharLoc(a)]) var hasDefault = false for i in splitPoint+1..= $2 && $1 <= $3) goto $4;$n", "if ($1 == $2) goto $3;$n") else: if t[0].kind == nkSym and sfGoto in t[0].sym.flags: genGotoForCase(p, t) else: genOrdinalCase(p, t, d) proc genRestoreFrameAfterException(p: BProc) = if optStackTrace in p.module.config.options: if hasCurFramePointer notin p.flags: p.flags.incl hasCurFramePointer p.procSec(cpsLocals).add(ropecg(p.module, "\tTFrame* _nimCurFrame;$n", [])) p.procSec(cpsInit).add(ropecg(p.module, "\t_nimCurFrame = #getFrame();$n", [])) linefmt(p, cpsStmts, "#setFrame(_nimCurFrame);$n", []) proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = #[ code to generate: std::exception_ptr error = nullptr; try { body; } catch (Exception e) { error = std::current_exception(); if (ofExpr(e, TypeHere)) { error = nullptr; // handled } else if (...) { } else { throw; } } catch(...) { // C++ exception occured, not under Nim's control. } { /* finally: */ printf('fin!\n'); if (error) std::rethrow_exception(error); // re-raise the exception } ]# p.module.includeHeader("") if not isEmptyType(t.typ) and d.k == locNone: getTemp(p, t.typ, d) genLineDir(p, t) inc(p.labels, 2) let etmp = p.labels p.procSec(cpsInit).add(ropecg(p.module, "\tstd::exception_ptr T$1_ = nullptr;", [etmp])) let fin = if t[^1].kind == nkFinally: t[^1] else: nil p.nestedTryStmts.add((fin, false, 0.Natural)) if t.kind == nkHiddenTryStmt: lineCg(p, cpsStmts, "try {$n", []) expr(p, t[0], d) lineCg(p, cpsStmts, "}$n", []) else: startBlock(p, "try {$n") expr(p, t[0], d) endBlock(p) # First pass: handle Nim based exceptions: lineCg(p, cpsStmts, "catch (#Exception* T$1_) {$n", [etmp+1]) genRestoreFrameAfterException(p) # an unhandled exception happened! lineCg(p, cpsStmts, "T$1_ = std::current_exception();$n", [etmp]) p.nestedTryStmts[^1].inExcept = true var hasImportedCppExceptions = false var i = 1 var hasIf = false var hasElse = false while (i < t.len) and (t[i].kind == nkExceptBranch): # bug #4230: avoid false sharing between branches: if d.k == locTemp and isEmptyType(t.typ): d.k = locNone if t[i].len == 1: hasImportedCppExceptions = true # general except section: hasElse = true if hasIf: lineF(p, cpsStmts, "else ", []) startBlock(p) # we handled the error: linefmt(p, cpsStmts, "T$1_ = nullptr;$n", [etmp]) expr(p, t[i][0], d) linefmt(p, cpsStmts, "#popCurrentException();$n", []) endBlock(p) else: var orExpr = Rope(nil) var exvar = PNode(nil) for j in 0..$1, $2)", [memberName, checkFor]) if orExpr != nil: if hasIf: startBlock(p, "else if ($1) {$n", [orExpr]) else: startBlock(p, "if ($1) {$n", [orExpr]) hasIf = true if exvar != nil: fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), OnStack) linefmt(p, cpsStmts, "$1 $2 = T$3_;$n", [getTypeDesc(p.module, exvar.sym.typ), rdLoc(exvar.sym.loc), rope(etmp+1)]) # we handled the error: linefmt(p, cpsStmts, "T$1_ = nullptr;$n", [etmp]) expr(p, t[i][^1], d) linefmt(p, cpsStmts, "#popCurrentException();$n", []) endBlock(p) inc(i) if hasIf and not hasElse: linefmt(p, cpsStmts, "else throw;$n", [etmp]) linefmt(p, cpsStmts, "}$n", []) # Second pass: handle C++ based exceptions: template genExceptBranchBody(body: PNode) {.dirty.} = genRestoreFrameAfterException(p) #linefmt(p, cpsStmts, "T$1_ = std::current_exception();$n", [etmp]) expr(p, body, d) var catchAllPresent = false incl p.flags, noSafePoints # mark as not needing 'popCurrentException' if hasImportedCppExceptions: for i in 1.. 0 and t[^1].kind == nkFinally: if not catchAllPresent: startBlock(p, "catch (...) {", []) genRestoreFrameAfterException(p) linefmt(p, cpsStmts, "T$1_ = std::current_exception();$n", [etmp]) endBlock(p) startBlock(p) genStmts(p, t[^1][0]) linefmt(p, cpsStmts, "if (T$1_) std::rethrow_exception(T$1_);$n", [etmp]) endBlock(p) proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) = # There are two versions we generate, depending on whether we # catch C++ exceptions, imported via .importcpp or not. The # code can be easier if there are no imported C++ exceptions # to deal with. # code to generate: # # try # { # myDiv(4, 9); # } catch (NimExceptionType1&) { # body # } catch (NimExceptionType2&) { # finallyPart() # raise; # } # catch(...) { # general_handler_body # } # finallyPart(); template genExceptBranchBody(body: PNode) {.dirty.} = genRestoreFrameAfterException(p) expr(p, body, d) if not isEmptyType(t.typ) and d.k == locNone: getTemp(p, t.typ, d) genLineDir(p, t) discard cgsym(p.module, "popCurrentExceptionEx") let fin = if t[^1].kind == nkFinally: t[^1] else: nil p.nestedTryStmts.add((fin, false, 0.Natural)) startBlock(p, "try {$n") expr(p, t[0], d) endBlock(p) var catchAllPresent = false p.nestedTryStmts[^1].inExcept = true for i in 1.. 1: lineF(p, cpsStmts, "else", []) startBlock(p) # we handled the exception, remember this: linefmt(p, cpsStmts, "*nimErr_ = NIM_FALSE;$n", []) expr(p, t[i][0], d) else: var orExpr: Rope = nil for j in 0..$1, $2)", [memberName, checkFor]) if i > 1: line(p, cpsStmts, "else ") startBlock(p, "if ($1) {$n", [orExpr]) # we handled the exception, remember this: linefmt(p, cpsStmts, "*nimErr_ = NIM_FALSE;$n", []) expr(p, t[i][^1], d) linefmt(p, cpsStmts, "#popCurrentException();$n", []) linefmt(p, cpsStmts, "LA$1_:;$n", [nextExcept]) endBlock(p) inc(i) discard pop(p.nestedTryStmts) endBlock(p) if i < t.len and t[i].kind == nkFinally: startBlock(p) if not bodyCanRaise(p, t[i][0]): # this is an important optimization; most destroy blocks are detected not to raise an # exception and so we help the C optimizer by not mutating nimErr_ pointlessly: genStmts(p, t[i][0]) else: # pretend we did handle the error for the safe execution of the 'finally' section: p.procSec(cpsLocals).add(ropecg(p.module, "NIM_BOOL oldNimErrFin$1_;$n", [lab])) linefmt(p, cpsStmts, "oldNimErrFin$1_ = *nimErr_; *nimErr_ = NIM_FALSE;$n", [lab]) genStmts(p, t[i][0]) # this is correct for all these cases: # 1. finally is run during ordinary control flow # 2. finally is run after 'except' block handling: these however set the # error back to nil. # 3. finally is run for exception handling code without any 'except' # handler present or only handlers that did not match. linefmt(p, cpsStmts, "*nimErr_ = oldNimErrFin$1_;$n", [lab]) endBlock(p) raiseExit(p) if hasExcept: inc p.withinTryWithExcept proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = # code to generate: # # XXX: There should be a standard dispatch algorithm # that's used both here and with multi-methods # # TSafePoint sp; # pushSafePoint(&sp); # sp.status = setjmp(sp.context); # if (sp.status == 0) { # myDiv(4, 9); # popSafePoint(); # } else { # popSafePoint(); # /* except DivisionByZero: */ # if (sp.status == DivisionByZero) { # printf('Division by Zero\n'); # clearException(); # } else { # clearException(); # } # } # { # /* finally: */ # printf('fin!\n'); # } # if (exception not cleared) # propagateCurrentException(); # if not isEmptyType(t.typ) and d.k == locNone: getTemp(p, t.typ, d) let quirkyExceptions = p.config.exc == excQuirky or (t.kind == nkHiddenTryStmt and sfSystemModule in p.module.module.flags) if not quirkyExceptions: p.module.includeHeader("") else: p.flags.incl noSafePoints genLineDir(p, t) discard cgsym(p.module, "Exception") var safePoint: Rope if not quirkyExceptions: safePoint = getTempName(p.module) linefmt(p, cpsLocals, "#TSafePoint $1;$n", [safePoint]) linefmt(p, cpsStmts, "#pushSafePoint(&$1);$n", [safePoint]) if isDefined(p.config, "nimStdSetjmp"): linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint]) elif isDefined(p.config, "nimSigSetjmp"): linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", [safePoint]) elif isDefined(p.config, "nimRawSetjmp"): linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", [safePoint]) else: linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint]) lineCg(p, cpsStmts, "if ($1.status == 0) {$n", [safePoint]) let fin = if t[^1].kind == nkFinally: t[^1] else: nil p.nestedTryStmts.add((fin, quirkyExceptions, 0.Natural)) expr(p, t[0], d) if not quirkyExceptions: linefmt(p, cpsStmts, "#popSafePoint();$n", []) lineCg(p, cpsStmts, "}$n", []) startBlock(p, "else {$n") linefmt(p, cpsStmts, "#popSafePoint();$n", []) genRestoreFrameAfterException(p) elif 1 < t.len and t[1].kind == nkExceptBranch: startBlock(p, "if (#nimBorrowCurrentException()) {$n") else: startBlock(p) p.nestedTryStmts[^1].inExcept = true var i = 1 while (i < t.len) and (t[i].kind == nkExceptBranch): # bug #4230: avoid false sharing between branches: if d.k == locTemp and isEmptyType(t.typ): d.k = locNone if t[i].len == 1: # general except section: if i > 1: lineF(p, cpsStmts, "else", []) startBlock(p) if not quirkyExceptions: linefmt(p, cpsStmts, "$1.status = 0;$n", [safePoint]) expr(p, t[i][0], d) linefmt(p, cpsStmts, "#popCurrentException();$n", []) endBlock(p) else: var orExpr: Rope = nil for j in 0..$1, $2)", [memberName, checkFor]) if i > 1: line(p, cpsStmts, "else ") startBlock(p, "if ($1) {$n", [orExpr]) if not quirkyExceptions: linefmt(p, cpsStmts, "$1.status = 0;$n", [safePoint]) expr(p, t[i][^1], d) linefmt(p, cpsStmts, "#popCurrentException();$n", []) endBlock(p) inc(i) discard pop(p.nestedTryStmts) endBlock(p) # end of else block if i < t.len and t[i].kind == nkFinally: p.finallySafePoints.add(safePoint) startBlock(p) genStmts(p, t[i][0]) # pretend we handled the exception in a 'finally' so that we don't # re-raise the unhandled one but instead keep the old one (it was # not popped either): if not quirkyExceptions and getCompilerProc(p.module.g.graph, "nimLeaveFinally") != nil: linefmt(p, cpsStmts, "if ($1.status != 0) #nimLeaveFinally();$n", [safePoint]) endBlock(p) discard pop(p.finallySafePoints) if not quirkyExceptions: linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", [safePoint]) proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope = var res = "" for it in t.sons: case it.kind of nkStrLit..nkTripleStrLit: res.add(it.strVal) of nkSym: var sym = it.sym if sym.kind in {skProc, skFunc, skIterator, skMethod}: var a: TLoc initLocExpr(p, it, a) res.add($rdLoc(a)) elif sym.kind == skType: res.add($getTypeDesc(p.module, sym.typ)) else: discard getTypeDesc(p.module, skipTypes(sym.typ, abstractPtrs)) var r = sym.loc.r if r == nil: # if no name has already been given, # it doesn't matter much: r = mangleName(p.module, sym) sym.loc.r = r # but be consequent! res.add($r) of nkTypeOfExpr: res.add($getTypeDesc(p.module, it.typ)) else: discard getTypeDesc(p.module, skipTypes(it.typ, abstractPtrs)) var a: TLoc initLocExpr(p, it, a) res.add($a.rdLoc) if isAsmStmt and hasGnuAsm in CC[p.config.cCompiler].props: for x in splitLines(res): var j = 0 while j < x.len and x[j] in {' ', '\t'}: inc(j) if j < x.len: if x[j] in {'"', ':'}: # don't modify the line if already in quotes or # some clobber register list: result.add(x); result.add("\L") else: # ignore empty lines result.add("\"") result.add(x.replace("\"", "\\\"")) result.add("\\n\"\n") else: res.add("\L") result = res.rope proc genAsmStmt(p: BProc, t: PNode) = assert(t.kind == nkAsmStmt) genLineDir(p, t) var s = genAsmOrEmitStmt(p, t, isAsmStmt=true) # 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 # work: if p.prc == nil: # top level asm statement? p.module.s[cfsProcHeaders].add runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s]) else: p.s(cpsStmts).add indentLine(p, runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s])) proc determineSection(n: PNode): TCFileSection = result = cfsProcHeaders if n.len >= 1 and n[0].kind in {nkStrLit..nkTripleStrLit}: let sec = n[0].strVal if sec.startsWith("/*TYPESECTION*/"): result = cfsTypes elif sec.startsWith("/*VARSECTION*/"): result = cfsVars elif sec.startsWith("/*INCLUDESECTION*/"): result = cfsHeaders proc genEmit(p: BProc, t: PNode) = var s = genAsmOrEmitStmt(p, t[1]) if p.prc == nil: # top level emit pragma? let section = determineSection(t[1]) genCLineDir(p.module.s[section], t.info, p.config) p.module.s[section].add(s) else: genLineDir(p, t) line(p, cpsStmts, s) proc genPragma(p: BProc, n: PNode) = for it in n.sons: case whichPragma(it) of wEmit: genEmit(p, it) of wInjectStmt: var p = newProc(nil, p.module) p.options.excl {optLineTrace, optStackTrace} genStmts(p, it[1]) p.module.injectStmt = p.s(cpsStmts) else: discard proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType, field: PSym) = var t = skipTypes(objtype, abstractVar) assert t.kind == tyObject discard genTypeInfoV1(p.module, t, a.lode.info) if not containsOrIncl(p.module.declaredThings, field.id): appcg(p.module, cfsVars, "extern $1", [discriminatorTableDecl(p.module, t, field)]) lineCg(p, cpsStmts, "#FieldDiscriminantCheck((NI)(NU)($1), (NI)(NU)($2), $3, $4);$n", [rdLoc(a), rdLoc(tmp), discriminatorTableName(p.module, t, field), intLiteral(toInt64(lengthOrd(p.config, field.typ))+1)]) when false: proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLoc) = const ObjDiscMappingProcSlot = -5 var theProc: PSym = nil for idx, p in items(t.methods): if idx == ObjDiscMappingProcSlot: theProc = p break if theProc == nil: theProc = genCaseObjDiscMapping(t, field, e.info, p.module.g.graph, p.module.idgen) t.methods.add((ObjDiscMappingProcSlot, theProc)) var call = newNodeIT(nkCall, e.info, getSysType(p.module.g.graph, e.info, tyUInt8)) call.add newSymNode(theProc) call.add e expr(p, call, d) proc asgnFieldDiscriminant(p: BProc, e: PNode) = var a, tmp: TLoc var dotExpr = e[0] if dotExpr.kind == nkCheckedFieldExpr: dotExpr = dotExpr[0] initLocExpr(p, e[0], a) getTemp(p, a.t, tmp) expr(p, e[1], tmp) if optTinyRtti notin p.config.globalOptions: let field = dotExpr[1].sym genDiscriminantCheck(p, a, tmp, dotExpr[0].typ, field) message(p.config, e.info, warnCaseTransition) genAssignment(p, a, tmp, {}) proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = if e[0].kind == nkSym and sfGoto in e[0].sym.flags: genLineDir(p, e) genGotoVar(p, e[1]) elif optFieldCheck in p.options and isDiscriminantField(e[0]): genLineDir(p, e) asgnFieldDiscriminant(p, e) else: let le = e[0] let ri = e[1] var a: TLoc discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs), skVar) initLoc(a, locNone, le, OnUnknown) a.flags.incl(lfEnforceDeref) a.flags.incl(lfPrepareForMutation) expr(p, le, a) a.flags.excl(lfPrepareForMutation) if fastAsgn: incl(a.flags, lfNoDeepCopy) assert(a.t != nil) genLineDir(p, ri) loadInto(p, le, ri, a) proc genStmts(p: BProc, t: PNode) = var a: TLoc let isPush = p.config.hasHint(hintExtendedContext) if isPush: pushInfoContext(p.config, t.info) expr(p, t, a) if isPush: popInfoContext(p.config) internalAssert p.config, a.k in {locNone, locTemp, locLocalVar, locExpr}