diff --git a/compiler/ast.nim b/compiler/ast.nim index a28a7e7e33..68bf957724 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -453,6 +453,8 @@ type nfPreventCg # this node should be ignored by the codegen nfBlockArg # this a stmtlist appearing in a call (e.g. a do block) nfFromTemplate # a top-level node returned from a template + nfPreventDestructor # prevent destructor injectsion for the node + # (but not necessarily its children) TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that) diff --git a/compiler/commands.nim b/compiler/commands.nim index 766b787980..2e9b6f8dbe 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -257,6 +257,7 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool = of "rangechecks": result = contains(gOptions, optRangeCheck) of "boundchecks": result = contains(gOptions, optBoundsCheck) of "overflowchecks": result = contains(gOptions, optOverflowCheck) + of "movechecks": result = contains(gOptions, optMoveCheck) of "linedir": result = contains(gOptions, optLineDir) of "assertions", "a": result = contains(gOptions, optAssert) of "deadcodeelim": result = contains(gGlobalOptions, optDeadCodeElim) @@ -493,6 +494,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "rangechecks": processOnOffSwitch({optRangeCheck}, arg, pass, info) of "boundchecks": processOnOffSwitch({optBoundsCheck}, arg, pass, info) of "overflowchecks": processOnOffSwitch({optOverflowCheck}, arg, pass, info) + of "movechecks": processOnOffSwitch({optMoveCheck}, arg, pass, info) of "linedir": processOnOffSwitch({optLineDir}, arg, pass, info) of "assertions", "a": processOnOffSwitch({optAssert}, arg, pass, info) of "deadcodeelim": processOnOffSwitchG({optDeadCodeElim}, arg, pass, info) diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index 55da69985a..9acc89be46 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -89,11 +89,36 @@ ## tmp.bar)) ## destroy(tmp.bar) ## destroy(tmp.x); destroy(tmp.y) +## +##[ +From https://github.com/nim-lang/Nim/wiki/Destructors + +Rule Pattern Transformed into +---- ------- ---------------- +1.1 var x: T; stmts var x: T; try stmts + finally: `=destroy`(x) +1.2 var x: sink T; stmts var x: sink T; stmts; ensureEmpty(x) +2 x = f() `=sink`(x, f()) +3 x = lastReadOf z `=sink`(x, z) +4.1 y = sinkParam `=sink`(y, sinkParam) +4.2 x = y `=`(x, y) # a copy +5.1 f_sink(g()) f_sink(g()) +5.2 f_sink(y) f_sink(copy y); # copy unless we can see it's the last read +5.3 f_sink(move y) f_sink(y); reset(y) # explicit moves empties 'y' +5.4 f_noSink(g()) var tmp = bitwiseCopy(g()); f(tmp); `=destroy`(tmp) + +Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently + not allowed as a local variable. + +``move`` builtin needs to be implemented. + +XXX Think about nfPreventDestructor logic. +]## import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, - strutils, options, dfa, lowerings, rodread + strutils, options, dfa, lowerings, rodread, tables const InterestingSyms = {skVar, skResult, skLet} @@ -106,6 +131,14 @@ type tmpObj: PType tmp: PSym destroys, topLevelVars: PNode + toDropBit: Table[int, PSym] + +proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode = + # XXX why are temps fields in an object here? + let f = newSym(skField, getIdent(":d" & $c.tmpObj.n.len), c.owner, info) + f.typ = typ + rawAddField c.tmpObj, f + result = rawDirectAccess(c.tmp, f) proc isHarmlessVar*(s: PSym; c: Con): bool = # 's' is harmless if it used only once and its @@ -212,14 +245,44 @@ proc genDestroy(t: PType; dest: PNode): PNode = proc addTopVar(c: var Con; v: PNode) = c.topLevelVars.add newTree(nkIdentDefs, v, emptyNode, emptyNode) +proc dropBit(c: var Con; s: PSym): PSym = + result = c.toDropBit.getOrDefault(s.id) + assert result != nil + +proc registerDropBit(c: var Con; s: PSym) = + let result = newSym(skTemp, getIdent(s.name.s & "_AliveBit"), c.owner, s.info) + result.typ = getSysType(tyBool) + let trueVal = newIntTypeNode(nkIntLit, 1, result.typ) + c.topLevelVars.add newTree(nkIdentDefs, newSymNode result, emptyNode, trueVal) + c.toDropBit[s.id] = result + # generate: + # if not sinkParam_AliveBit: `=destroy`(sinkParam) + c.destroys.add newTree(nkIfStmt, + newTree(nkElifBranch, newSymNode result, genDestroy(s.typ, newSymNode s))) + proc p(n: PNode; c: var Con): PNode template recurse(n, dest) = for i in 0.. 0: c.addTopVar(newSymNode c.tmp) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 09413128b0..70504cfc94 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -134,7 +134,7 @@ type hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, hintConditionAlwaysTrue, hintName, hintPattern, hintExecuting, hintLinking, hintDependency, - hintSource, hintStackTrace, hintGCStats, + hintSource, hintPerformance, hintStackTrace, hintGCStats, hintUser, hintUserRaw const @@ -438,6 +438,7 @@ const hintLinking: "", hintDependency: "$1", hintSource: "$1", + hintPerformance: "$1", hintStackTrace: "$1", hintGCStats: "$1", hintUser: "$1", @@ -460,7 +461,7 @@ const "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency", - "Source", "StackTrace", "GCStats", + "Source", "Performance", "StackTrace", "GCStats", "User", "UserRaw"] const diff --git a/compiler/options.nim b/compiler/options.nim index 65f5a62455..be13e15d7c 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -25,7 +25,7 @@ type # please make sure we have under 32 options TOption* = enum # **keep binary compatible** optNone, optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, optOverflowCheck, optNilCheck, - optNaNCheck, optInfCheck, + optNaNCheck, optInfCheck, optMoveCheck, optAssert, optLineDir, optWarns, optHints, optOptimizeSpeed, optOptimizeSize, optStackTrace, # stack tracing support optLineTrace, # line tracing support (includes stack tracing) @@ -119,13 +119,14 @@ var const ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck, - optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck} + optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck, + optMoveCheck} var gOptions*: TOptions = {optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, optOverflowCheck, optAssert, optWarns, optHints, optStackTrace, optLineTrace, - optPatterns, optNilCheck} + optPatterns, optNilCheck, optMoveCheck} gGlobalOptions*: TGlobalOptions = {optThreadAnalysis} gExitcode*: int8 gCmd*: TCommands = cmdNone # the command diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 1b80786280..1295ee18df 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -40,7 +40,8 @@ const wTags, wLocks, wGcSafe, wExportNims, wUsed} exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe} stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, - wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, + wBoundchecks, wOverflowchecks, wNilchecks, wMovechecks, wAssertions, + wWarnings, wHints, wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, wFatal, wDefine, wUndef, wCompile, wLink, wLinksys, wPure, wPush, wPop, wBreakpoint, wWatchPoint, wPassl, wPassc, wDeadCodeElim, wDeprecated, @@ -323,6 +324,7 @@ proc processOption(c: PContext, n: PNode): bool = of wFloatchecks: onOff(c, n, {optNaNCheck, optInfCheck}) of wNanChecks: onOff(c, n, {optNaNCheck}) of wInfChecks: onOff(c, n, {optInfCheck}) + of wMovechecks: onOff(c, n, {optMoveCheck}) of wAssertions: onOff(c, n, {optAssert}) of wWarnings: onOff(c, n, {optWarns}) of wHints: onOff(c, n, {optHints}) @@ -693,7 +695,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, inc c.instCounter if c.instCounter > 100: globalError(it.info, errRecursiveDependencyX, userPragma.name.s) - + pragma(c, sym, userPragma.ast, validPragmas) 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 @@ -906,7 +908,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wCodegenDecl: processCodegenDecl(c, it, sym) of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, - wLinedir, wStacktrace, wLinetrace, wOptimization, + wLinedir, wStacktrace, wLinetrace, wOptimization, wMovechecks, wCallconv, wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks, wPatterns: diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 63a4eb99ae..e2296a0d23 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -76,7 +76,7 @@ proc semConstrField(c: PContext, flags: TExprFlags, return initValue proc caseBranchMatchesExpr(branch, matched: PNode): bool = - for i in 0 .. (branch.len - 2): + for i in 0 .. branch.len-2: if exprStructuralEquivalent(branch[i], matched): return true diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index e458cad03f..76d91d4e77 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -52,7 +52,7 @@ type wNimcall, wStdcall, wCdecl, wSafecall, wSyscall, wInline, wNoInline, wFastcall, wClosure, wNoconv, wOn, wOff, wChecks, wRangechecks, wBoundchecks, wOverflowchecks, wNilchecks, - wFloatchecks, wNanChecks, wInfChecks, + wFloatchecks, wNanChecks, wInfChecks, wMoveChecks, wAssertions, wPatterns, wWarnings, wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags, wDeadCodeElim, wSafecode, wPackage, wNoForward, wReorder, wNoRewrite, @@ -139,7 +139,7 @@ const "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "closure", "noconv", "on", "off", "checks", "rangechecks", "boundchecks", "overflowchecks", "nilchecks", - "floatchecks", "nanchecks", "infchecks", + "floatchecks", "nanchecks", "infchecks", "movechecks", "assertions", "patterns", "warnings", "hints", "optimization", "raises", "writes", "reads", "size", "effects", "tags", diff --git a/lib/system.nim b/lib/system.nim index 7116f88184..7eda302768 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -617,6 +617,11 @@ type ## ## This is only raised if the ``segfaults.nim`` module was imported! +when defined(nimNewRuntime): + type + MoveError* = object of SystemError ## \ + ## Raised on attempts to re-sink an already consumed ``sink`` parameter. + {.deprecated: [TObject: RootObj, PObject: RootRef, TEffect: RootEffect, FTime: TimeEffect, FIO: IOEffect, FReadIO: ReadIOEffect, FWriteIO: WriteIOEffect, FExecIO: ExecIOEffect, diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim index 69b680dbdc..d3651f6590 100644 --- a/lib/system/chcks.nim +++ b/lib/system/chcks.nim @@ -52,6 +52,11 @@ proc chckNil(p: pointer) = if p == nil: sysFatal(NilAccessError, "attempt to write to a nil address") +when defined(nimNewRuntime): + proc chckMove(b: bool) {.compilerproc.} = + if not b: + sysFatal(MoveError, "attempt to access an object that was moved") + proc chckNilDisp(p: pointer) {.compilerproc.} = if p == nil: sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil")