From b35ee82c487bf3d2931260234053302d1ad96de9 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 25 Feb 2018 17:28:47 +0100 Subject: fixes #6992 --- tests/iter/titer2.nim | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'tests/iter') diff --git a/tests/iter/titer2.nim b/tests/iter/titer2.nim index 4a7f76883..f60aed73a 100644 --- a/tests/iter/titer2.nim +++ b/tests/iter/titer2.nim @@ -2,7 +2,12 @@ discard """ output: '''true 3 4 -5''' +5 +0 +1 +2 +3 +4''' cmd: "nim $target --gc:none --hints:on --warnings:off $options $file" """ @@ -55,3 +60,7 @@ echo "true" # bug #1560 for i in @[3, 4, 5]: echo($i) + +# bug #6992 +for i in 0 ..< 5u32: + echo i -- cgit 1.4.1-2-gfad0 From 72dfe176f5211f561263984a2df653f16dcf5466 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Mon, 23 Apr 2018 17:02:38 +0800 Subject: remove dead code elimination option (#7669) --- compiler/ast.nim | 6 +++--- compiler/ccgexprs.nim | 5 ++--- compiler/ccgstmts.nim | 18 ++---------------- compiler/ccgutils.nim | 4 ---- compiler/cgen.nim | 4 ---- compiler/commands.nim | 3 +-- compiler/options.nim | 3 ++- compiler/pragmas.nim | 14 ++++---------- compiler/wordrecg.nim | 6 ++++-- doc/basicopt.txt | 1 - doc/manual.rst | 16 ---------------- doc/nimc.rst | 4 ++-- lib/deprecated/pure/sockets.nim | 2 +- lib/impure/db_sqlite.nim | 2 +- lib/impure/rdstdin.nim | 2 +- lib/posix/epoll.nim | 2 +- lib/posix/inotify.nim | 2 +- lib/posix/kqueue.nim | 8 +++----- lib/posix/linux.nim | 2 +- lib/posix/posix.nim | 8 ++++---- lib/posix/posix_other.nim | 2 +- lib/posix/termios.nim | 2 +- lib/pure/fenv.nim | 2 +- lib/pure/matchers.nim | 2 +- lib/pure/net.nim | 2 +- lib/pure/os.nim | 2 +- lib/pure/parseutils.nim | 2 +- lib/pure/ropes.nim | 2 +- lib/pure/strmisc.nim | 2 +- lib/pure/strutils.nim | 2 +- lib/pure/unicode.nim | 2 +- lib/system.nim | 2 +- lib/windows/winlean.nim | 2 +- lib/wrappers/iup.nim | 2 +- lib/wrappers/mysql.nim | 2 +- lib/wrappers/odbcsql.nim | 2 +- lib/wrappers/openssl.nim | 2 +- lib/wrappers/pcre.nim | 2 +- lib/wrappers/postgres.nim | 2 +- lib/wrappers/sqlite3.nim | 2 +- tests/enum/toptions.nim | 2 +- tests/iter/tobj_iter.nim | 2 -- .../keineschweine/dependencies/chipmunk/chipmunk.nim | 1 - tests/manyloc/keineschweine/dependencies/enet/enet.nim | 1 - .../keineschweine/dependencies/genpacket/macro_dsl.nim | 2 +- tests/manyloc/keineschweine/dependencies/sfml/sfml.nim | 2 +- .../keineschweine/dependencies/sfml/sfml_colors.nim | 2 +- .../keineschweine/dependencies/sfml/sfml_vector.nim | 1 - tests/manyloc/keineschweine/keineschweine.nim | 2 +- tests/manyloc/keineschweine/lib/sg_gui.nim | 2 +- tests/manyloc/keineschweine/server/nim.cfg | 1 - tests/manyloc/nake/nakefile.nim | 2 +- tests/manyloc/standalone/barebone.nim.cfg | 1 - tests/pragmas/tnoreturn.nim | 7 +++++-- tests/rodfiles/deadg.nim | 3 --- tools/nim.bash-completion | 4 ++-- tools/nim.zsh-completion | 2 -- 57 files changed, 67 insertions(+), 122 deletions(-) (limited to 'tests/iter') diff --git a/compiler/ast.nim b/compiler/ast.nim index 4a0a9a20b..2c8f686eb 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -262,7 +262,8 @@ type # variable is a thread variable sfCompileTime, # proc can be evaluated at compile time sfConstructor, # proc is a C++ constructor - sfDeadCodeElim, # dead code elimination for the module is turned on + sfDispatcher, # copied method symbol is the dispatcher + # deprecated and unused, except for the con sfBorrow, # proc is borrowed sfInfixCall, # symbol needs infix call syntax in target language; # for interfacing with C++, JS @@ -275,10 +276,9 @@ type TSymFlags* = set[TSymFlag] const - sfDispatcher* = sfDeadCodeElim # copied method symbol is the dispatcher sfNoInit* = sfMainModule # don't generate code to init the variable - sfImmediate* = sfDeadCodeElim + sfImmediate* = sfDispatcher # macro or template is immediately expanded # without considering any possible overloads sfAllUntyped* = sfVolatile # macro or template is immediately expanded \ diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 461a86298..ea373f5a6 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2264,7 +2264,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkEmpty: discard of nkWhileStmt: genWhileStmt(p, n) of nkVarSection, nkLetSection: genVarStmt(p, n) - of nkConstSection: genConstStmt(p, n) + of nkConstSection: discard # consts generated lazily on use of nkForStmt: internalError(n.info, "for statement not eliminated") of nkCaseStmt: genCase(p, n, d) of nkReturnStmt: genReturnStmt(p, n) @@ -2315,8 +2315,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = # are not transformed correctly. We work around this issue (#411) here # by ensuring it's no inner proc (owner is a module): if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags: - if (not emitLazily(prc)) or - ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or + if ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or (prc.kind == skMethod): # we have not only the header: diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 2030d6add..cb3d6dbe6 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -280,20 +280,6 @@ proc genVarStmt(p: BProc, n: PNode) = else: genVarTuple(p, it) -proc genConstStmt(p: BProc, n: PNode) = - for it in n.sons: - if it.kind == nkCommentStmt: continue - if it.kind != nkConstDef: internalError(n.info, "genConstStmt") - - let sym = it.sons[0].sym - if sym.typ.containsCompileTimeOnly or - sym.typ.kind notin ConstantDataTypes or - sym.ast.len == 0 or - emitLazily(sym): - continue - - requestConstImpl(p, sym) - proc genIf(p: BProc, n: PNode, d: var TLoc) = # # { if (!expr1) goto L1; @@ -587,7 +573,7 @@ proc genRaiseStmt(p: BProc, t: PNode) = genLineDir(p, t) if isImportedException(typ): lineF(p, cpsStmts, "throw $1;$n", [e]) - else: + else: lineCg(p, cpsStmts, "#raiseException((#Exception*)$1, $2);$n", [e, makeCString(typ.sym.name.s)]) else: @@ -836,7 +822,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = else: for j in 0..t[i].len-2: if t[i][j].isInfixAs(): - let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:` + let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:` fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), OnUnknown) startBlock(p, "catch ($1& $2) {$n", getTypeDesc(p.module, t[i][j][1].typ), rdLoc(exvar.sym.loc)) else: diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index fe28d2209..48648bdde 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -211,8 +211,4 @@ proc mangle*(name: string): string = if requiresUnderscore: result.add "_" -proc emitLazily*(s: PSym): bool {.inline.} = - result = optDeadCodeElim in gGlobalOptions or - sfDeadCodeElim in getModule(s).flags - initTypeTables() diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 9e1f9349f..ff3e6714d 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1309,10 +1309,6 @@ proc newModule(g: BModuleList; module: PSym): BModule = growCache g.modules, module.position g.modules[module.position] = result - if (optDeadCodeElim in gGlobalOptions): - if (sfDeadCodeElim in module.flags): - internalError("added pending module twice: " & toFilename(FileIndex module.position)) - template injectG(config) {.dirty.} = if graph.backend == nil: graph.backend = newModuleList(config) diff --git a/compiler/commands.nim b/compiler/commands.nim index e1dc1aacf..8d73ac90e 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -268,7 +268,6 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool = 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) of "run", "r": result = contains(gGlobalOptions, optRun) of "symbolfiles": result = gSymbolFiles != disabledSf of "genscript": result = contains(gGlobalOptions, optGenScript) @@ -509,7 +508,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; 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) + of "deadcodeelim": discard # deprecated, dead code elim always on of "threads": processOnOffSwitchG({optThreads}, arg, pass, info) #if optThreads in gGlobalOptions: incl(gNotes, warnGcUnsafe) diff --git a/compiler/options.nim b/compiler/options.nim index 93a3f1796..7126d4398 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -41,7 +41,8 @@ type # please make sure we have under 32 options TOptions* = set[TOption] TGlobalOption* = enum # **keep binary compatible** - gloptNone, optForceFullMake, optDeadCodeElim, + gloptNone, optForceFullMake, + optDeadCodeElimUnused, # deprecated, always on optListCmd, optCompileOnly, optNoLinking, optCDebug, # turn on debugging information optGenDynLib, # generate a dynamic library diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index cb11564a4..9e9233fe7 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -44,7 +44,9 @@ const wWarnings, wHints, wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, wFatal, wDefine, wUndef, wCompile, wLink, wLinksys, wPure, wPush, wPop, - wBreakpoint, wWatchPoint, wPassl, wPassc, wDeadCodeElim, wDeprecated, + wBreakpoint, wWatchPoint, wPassl, wPassc, + wDeadCodeElimUnused, # deprecated, always on + wDeprecated, wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll, wLinearScanEnd, wPatterns, wEffects, wNoForward, wReorder, wComputedGoto, wInjectStmt, wDeprecated, wExperimental, wThis} @@ -215,10 +217,6 @@ proc onOff(c: PContext, n: PNode, op: TOptions) = if isTurnedOn(c, n): gOptions = gOptions + op else: gOptions = gOptions - op -proc pragmaDeadCodeElim(c: PContext, n: PNode) = - if isTurnedOn(c, n): incl(c.module.flags, sfDeadCodeElim) - else: excl(c.module.flags, sfDeadCodeElim) - proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) = if isTurnedOn(c, n): incl(c.module.flags, flag) else: excl(c.module.flags, flag) @@ -764,7 +762,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wThreadVar: noVal(it) incl(sym.flags, sfThread) - of wDeadCodeElim: pragmaDeadCodeElim(c, it) + of wDeadCodeElimUnused: discard # deprecated, dead code elim always on of wNoForward: pragmaNoForward(c, it) of wReorder: pragmaNoForward(c, it, sfReorder) of wMagic: processMagic(c, it, sym) @@ -960,10 +958,6 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, if sym.kind != skType or sym.typ == nil: invalidPragma(it) else: incl(sym.typ.flags, tfPartial) - # .partial types can only work with dead code elimination - # to prevent the codegen from doing anything before we compiled - # the whole program: - incl gGlobalOptions, optDeadCodeElim of wInject, wGensym: # We check for errors, but do nothing with these pragmas otherwise # as they are handled directly in 'evalTemplate'. diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 76d91d4e7..91b527e02 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -55,7 +55,8 @@ type wFloatchecks, wNanChecks, wInfChecks, wMoveChecks, wAssertions, wPatterns, wWarnings, wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags, - wDeadCodeElim, wSafecode, wPackage, wNoForward, wReorder, wNoRewrite, + wDeadCodeElimUnused, # deprecated, dead code elim always happens + wSafecode, wPackage, wNoForward, wReorder, wNoRewrite, wPragma, wCompileTime, wNoInit, wPassc, wPassl, wBorrow, wDiscardable, @@ -143,7 +144,8 @@ const "assertions", "patterns", "warnings", "hints", "optimization", "raises", "writes", "reads", "size", "effects", "tags", - "deadcodeelim", "safecode", "package", "noforward", "reorder", "norewrite", + "deadcodeelim", # deprecated, dead code elim always happens + "safecode", "package", "noforward", "reorder", "norewrite", "pragma", "compiletime", "noinit", "passc", "passl", "borrow", "discardable", "fieldchecks", diff --git a/doc/basicopt.txt b/doc/basicopt.txt index 4c11cc767..90c7ba09c 100644 --- a/doc/basicopt.txt +++ b/doc/basicopt.txt @@ -29,7 +29,6 @@ Options: --nanChecks:on|off turn NaN checks on|off --infChecks:on|off turn Inf checks on|off --nilChecks:on|off turn nil checks on|off - --deadCodeElim:on|off whole program dead code elimination on|off --opt:none|speed|size optimize not at all or for speed|size Note: use -d:release for a release build! --debugger:native|endb use native debugger (gdb) | ENDB (experimental) diff --git a/doc/manual.rst b/doc/manual.rst index fbd043020..f1330d524 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -6768,22 +6768,6 @@ the created global variables within a module is not defined, but all of them will be initialized after any top-level variables in their originating module and before any variable in a module that imports it. -deadCodeElim pragma -------------------- -The ``deadCodeElim`` pragma only applies to whole modules: It tells the -compiler to activate (or deactivate) dead code elimination for the module the -pragma appears in. - -The ``--deadCodeElim:on`` command line switch has the same effect as marking -every module with ``{.deadCodeElim:on}``. However, for some modules such as -the GTK wrapper it makes sense to *always* turn on dead code elimination - -no matter if it is globally active or not. - -Example: - -.. code-block:: nim - {.deadCodeElim: on.} - .. NoForward pragma diff --git a/doc/nimc.rst b/doc/nimc.rst index b275438ea..29dbdea42 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -181,7 +181,7 @@ generated; use the ``--symbolFiles:on`` command line switch to activate them. Unfortunately due to technical reasons the ``--symbolFiles:on`` needs to *aggregate* some generated C code. This means that the resulting executable -might contain some cruft even when dead code elimination is turned on. So +might contain some cruft even with dead code elimination. So the final release build should be done with ``--symbolFiles:off``. Due to the aggregation of C code it is also recommended that each project @@ -439,7 +439,7 @@ target. For example, to generate code for an `AVR`:idx: processor use this command:: - nim c --cpu:avr --os:standalone --deadCodeElim:on --genScript x.nim + nim c --cpu:avr --os:standalone --genScript x.nim For the ``standalone`` target one needs to provide a file ``panicoverride.nim``. diff --git a/lib/deprecated/pure/sockets.nim b/lib/deprecated/pure/sockets.nim index f0568366a..05aebef76 100644 --- a/lib/deprecated/pure/sockets.nim +++ b/lib/deprecated/pure/sockets.nim @@ -32,7 +32,7 @@ include "system/inclrtl" -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated when hostOS == "solaris": {.passl: "-lsocket -lnsl".} diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index 21049571f..f88037e2f 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -81,7 +81,7 @@ ## ## theDb.close() -{.deadCodeElim:on.} +{.deadCodeElim: on.} # dce option deprecated import strutils, sqlite3 diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim index 5aa4cfcc3..54bab82f0 100644 --- a/lib/impure/rdstdin.nim +++ b/lib/impure/rdstdin.nim @@ -13,7 +13,7 @@ ## is used. This suffices because Windows' console already provides the ## wanted functionality. -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated when defined(Windows): proc readLineFromStdin*(prompt: string): TaintedString {. diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim index c5ed1a873..2d38137bb 100644 --- a/lib/posix/epoll.nim +++ b/lib/posix/epoll.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -{.deadCodeElim:on.} +{.deadCodeElim: on.} # dce option deprecated from posix import SocketHandle diff --git a/lib/posix/inotify.nim b/lib/posix/inotify.nim index a206f8067..359e88617 100644 --- a/lib/posix/inotify.nim +++ b/lib/posix/inotify.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -{.deadCodeElim:on.} +{.deadCodeElim: on.} # dce option deprecated # Get the platform-dependent flags. # Structure describing an inotify event. diff --git a/lib/posix/kqueue.nim b/lib/posix/kqueue.nim index 730491a53..18b47f5d5 100644 --- a/lib/posix/kqueue.nim +++ b/lib/posix/kqueue.nim @@ -7,8 +7,6 @@ # distribution, for details about the copyright. # -{.deadCodeElim:on.} - from posix import Timespec when defined(macosx) or defined(freebsd) or defined(openbsd) or @@ -61,7 +59,7 @@ const EV_CLEAR* = 0x0020 ## Clear event state after reporting. EV_RECEIPT* = 0x0040 ## Force EV_ERROR on success, data == 0 EV_DISPATCH* = 0x0080 ## Disable event after reporting. - + EV_SYSFLAGS* = 0xF000 ## Reserved by system EV_DROP* = 0x1000 ## Not should be dropped EV_FLAG1* = 0x2000 ## Filter-specific flag @@ -87,10 +85,10 @@ when defined(macosx) or defined(freebsd) or defined(dragonfly): NOTE_FFAND* = 0x40000000'u32 ## AND fflags NOTE_FFOR* = 0x80000000'u32 ## OR fflags NOTE_FFCOPY* = 0xc0000000'u32 ## copy fflags - NOTE_FFCTRLMASK* = 0xc0000000'u32 ## masks for operations + NOTE_FFCTRLMASK* = 0xc0000000'u32 ## masks for operations NOTE_FFLAGSMASK* = 0x00ffffff'u32 - NOTE_TRIGGER* = 0x01000000'u32 ## Cause the event to be triggered + NOTE_TRIGGER* = 0x01000000'u32 ## Cause the event to be triggered ## for output. # data/hint flags for EVFILT_{READ|WRITE}, shared with userspace diff --git a/lib/posix/linux.nim b/lib/posix/linux.nim index 6f9f75e34..25c7c7979 100644 --- a/lib/posix/linux.nim +++ b/lib/posix/linux.nim @@ -1,4 +1,4 @@ -{.deadCodeElim:on.} +{.deadCodeElim: on.} # dce option deprecated import posix diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index a23d76050..3ff156bdf 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -27,10 +27,10 @@ ## resulting C code will just ``#include `` and *not* define the ## symbols declared here. -# This ensures that we don't accidentally generate #includes for files that -# might not exist on a specific platform! The user will get an error only -# if they actualy try to use the missing declaration -{.deadCodeElim: on.} +# Dead code elimination ensures that we don't accidentally generate #includes +# for files that might not exist on a specific platform! The user will get an +# error only if they actualy try to use the missing declaration +{.deadCodeElim: on.} # dce option deprecated # TODO these constants don't seem to be fetched from a header file for unknown # platforms - where do they come from and why are they here? diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index ae41263e8..004a4205b 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -{.deadCodeElim:on.} +{.deadCodeElim: on.} # dce option deprecated const hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays diff --git a/lib/posix/termios.nim b/lib/posix/termios.nim index f86e408fb..60d540107 100644 --- a/lib/posix/termios.nim +++ b/lib/posix/termios.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated import posix type diff --git a/lib/pure/fenv.nim b/lib/pure/fenv.nim index f8f115ecc..a083c680c 100644 --- a/lib/pure/fenv.nim +++ b/lib/pure/fenv.nim @@ -10,7 +10,7 @@ ## Floating-point environment. Handling of floating-point rounding and ## exceptions (overflow, division by zero, etc.). -{.deadCodeElim:on.} +{.deadCodeElim: on.} # dce option deprecated when defined(Posix) and not defined(haiku): {.passl: "-lm".} diff --git a/lib/pure/matchers.nim b/lib/pure/matchers.nim index 6366fee1a..685c3b07a 100644 --- a/lib/pure/matchers.nim +++ b/lib/pure/matchers.nim @@ -12,7 +12,7 @@ ## **Warning:** This module is deprecated since version 0.14.0. {.deprecated.} -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated {.push debugger:off .} # the user does not want to trace a part # of the standard library! diff --git a/lib/pure/net.nim b/lib/pure/net.nim index ccf02a1fc..ebb59ca6d 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -68,7 +68,7 @@ ## ``newSocket()``. The difference is that the latter creates a new file ## descriptor. -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated import nativesockets, os, strutils, parseutils, times, sets, options export Port, `$`, `==` export Domain, SockType, Protocol diff --git a/lib/pure/os.nim b/lib/pure/os.nim index ddeee2c6b..11be8f0c1 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -10,7 +10,7 @@ ## This module contains basic operating system facilities like ## retrieving environment variables, reading command line arguments, ## working with directories, running shell commands, etc. -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated {.push debugger: off.} diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index 57387e62e..cecc94e92 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -11,7 +11,7 @@ ## ## To unpack raw bytes look at the `streams `_ module. -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated {.push debugger:off .} # the user does not want to trace a part # of the standard library! diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index 6ddd61afa..24ba012e5 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -19,7 +19,7 @@ include "system/inclrtl" import streams -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated {.push debugger:off .} # the user does not want to trace a part # of the standard library! diff --git a/lib/pure/strmisc.nim b/lib/pure/strmisc.nim index 89ef2fcd2..d1ff920c9 100644 --- a/lib/pure/strmisc.nim +++ b/lib/pure/strmisc.nim @@ -12,7 +12,7 @@ import strutils -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated proc expandTabs*(s: string, tabSize: int = 8): string {.noSideEffect, procvar.} = diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 54fdcb4d0..849c16c34 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -17,7 +17,7 @@ import parseutils from math import pow, round, floor, log10 from algorithm import reverse -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated {.push debugger:off .} # the user does not want to trace a part # of the standard library! diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 257c620f7..cc4d28f5b 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -9,7 +9,7 @@ ## This module provides support to handle the Unicode UTF-8 encoding. -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated include "system/inclrtl" diff --git a/lib/system.nim b/lib/system.nim index 5e08dadc0..874b8c4d1 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -146,7 +146,7 @@ proc declared*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.} ## # missing it. when defined(useNimRtl): - {.deadCodeElim: on.} + {.deadCodeElim: on.} # dce option deprecated proc definedInScope*(x: untyped): bool {. magic: "DefinedInScope", noSideEffect, deprecated, compileTime.} diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 62bc38da9..7e22f98c7 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -10,7 +10,7 @@ ## This module implements a small wrapper for some needed Win API procedures, ## so that the Nim compiler does not depend on the huge Windows module. -{.deadCodeElim:on.} +{.deadCodeElim: on.} # dce option deprecated import dynlib diff --git a/lib/wrappers/iup.nim b/lib/wrappers/iup.nim index d910173ca..dee2e14c7 100644 --- a/lib/wrappers/iup.nim +++ b/lib/wrappers/iup.nim @@ -34,7 +34,7 @@ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # **************************************************************************** -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated when defined(windows): const dllname = "iup(|30|27|26|25|24).dll" diff --git a/lib/wrappers/mysql.nim b/lib/wrappers/mysql.nim index 4464eae50..06c663822 100644 --- a/lib/wrappers/mysql.nim +++ b/lib/wrappers/mysql.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated {.push, callconv: cdecl.} when defined(Unix): diff --git a/lib/wrappers/odbcsql.nim b/lib/wrappers/odbcsql.nim index 1b2544ec0..43d1c504d 100644 --- a/lib/wrappers/odbcsql.nim +++ b/lib/wrappers/odbcsql.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated when not defined(ODBCVER): const diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 357343bff..86ae85369 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -22,7 +22,7 @@ ## ./bin/nim c -d:ssl -p:. -r tests/untestable/tssl.nim ## ./bin/nim c -d:ssl -p:. --dynlibOverride:ssl --passL:-lcrypto --passL:-lssl -r tests/untestable/tssl.nim -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated const useWinVersion = defined(Windows) or defined(nimdoc) diff --git a/lib/wrappers/pcre.nim b/lib/wrappers/pcre.nim index 6c7088bbf..e9e11960c 100644 --- a/lib/wrappers/pcre.nim +++ b/lib/wrappers/pcre.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated # The current PCRE version information. diff --git a/lib/wrappers/postgres.nim b/lib/wrappers/postgres.nim index f9a10dccb..7cb816f68 100644 --- a/lib/wrappers/postgres.nim +++ b/lib/wrappers/postgres.nim @@ -15,7 +15,7 @@ # connection-protocol. # -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated when defined(windows): const diff --git a/lib/wrappers/sqlite3.nim b/lib/wrappers/sqlite3.nim index a12945832..0276a0a65 100644 --- a/lib/wrappers/sqlite3.nim +++ b/lib/wrappers/sqlite3.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -{.deadCodeElim: on.} +{.deadCodeElim: on.} # dce option deprecated when defined(windows): when defined(nimOldDlls): const Lib = "sqlite3.dll" diff --git a/tests/enum/toptions.nim b/tests/enum/toptions.nim index e53acb2b3..da66f0067 100644 --- a/tests/enum/toptions.nim +++ b/tests/enum/toptions.nim @@ -4,7 +4,7 @@ type TOption = enum optNone, optForceFullMake, optBoehmGC, optRefcGC, optRangeCheck, optBoundsCheck, optOverflowCheck, optNilCheck, optAssert, optLineDir, - optWarns, optHints, optDeadCodeElim, optListCmd, optCompileOnly, + optWarns, optHints, optListCmd, optCompileOnly, optSafeCode, # only allow safe code optStyleCheck, optOptimizeSpeed, optOptimizeSize, optGenDynLib, optGenGuiApp, optStackTrace diff --git a/tests/iter/tobj_iter.nim b/tests/iter/tobj_iter.nim index eb0e37b23..a894755d7 100644 --- a/tests/iter/tobj_iter.nim +++ b/tests/iter/tobj_iter.nim @@ -4,8 +4,6 @@ discard """ # bug #2023 -{.deadCodeElim:on.} - type Obj = object iter: iterator (): int8 {.closure.} diff --git a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim index 56d3edec4..f06c4e0be 100644 --- a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim +++ b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim @@ -23,7 +23,6 @@ const Lib = "libchipmunk.so.6.1.1" when defined(MoreNim): {.hint: "MoreNim defined; some Chipmunk functions replaced in Nim".} -{.deadCodeElim: on.} from math import sqrt, sin, cos, arctan2 when defined(CpUseFloat): {.hint: "CpUseFloat defined; using float32 as float".} diff --git a/tests/manyloc/keineschweine/dependencies/enet/enet.nim b/tests/manyloc/keineschweine/dependencies/enet/enet.nim index 3c4ce2017..3ea8172d5 100644 --- a/tests/manyloc/keineschweine/dependencies/enet/enet.nim +++ b/tests/manyloc/keineschweine/dependencies/enet/enet.nim @@ -20,7 +20,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. const Lib = "libenet.so.1(|.0.3)" -{.deadCodeElim: on.} const ENET_VERSION_MAJOR* = 1 ENET_VERSION_MINOR* = 3 diff --git a/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim b/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim index d3a0c701d..86c12fbb0 100644 --- a/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim +++ b/tests/manyloc/keineschweine/dependencies/genpacket/macro_dsl.nim @@ -1,5 +1,5 @@ import macros -{.deadCodeElim: on.} + #Inline macro.add() to allow for easier nesting proc und*(a: NimNode; b: NimNode): NimNode {.compileTime.} = a.add(b) diff --git a/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim b/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim index 1524f0eb4..0060bf12b 100644 --- a/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim +++ b/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim @@ -12,7 +12,7 @@ else: LibS = "libcsfml-system.so.2.0" LibW = "libcsfml-window.so.2.0" #{.error: "Platform unsupported".} -{.deadCodeElim: on.} + {.pragma: pf, pure, final.} type PClock* = ptr TClock diff --git a/tests/manyloc/keineschweine/dependencies/sfml/sfml_colors.nim b/tests/manyloc/keineschweine/dependencies/sfml/sfml_colors.nim index 95a760e1f..b4eb1b8f0 100644 --- a/tests/manyloc/keineschweine/dependencies/sfml/sfml_colors.nim +++ b/tests/manyloc/keineschweine/dependencies/sfml/sfml_colors.nim @@ -1,5 +1,5 @@ import sfml -{.deadCodeElim: on.} + let Black*: TColor = color(0, 0, 0) White*: TColor = color(255, 255, 255) diff --git a/tests/manyloc/keineschweine/dependencies/sfml/sfml_vector.nim b/tests/manyloc/keineschweine/dependencies/sfml/sfml_vector.nim index 474d249aa..94c5308a9 100644 --- a/tests/manyloc/keineschweine/dependencies/sfml/sfml_vector.nim +++ b/tests/manyloc/keineschweine/dependencies/sfml/sfml_vector.nim @@ -1,2 +1 @@ import sfml, math, strutils -{.deadCodeElim: on.} diff --git a/tests/manyloc/keineschweine/keineschweine.nim b/tests/manyloc/keineschweine/keineschweine.nim index c0f1bc031..59347ee46 100644 --- a/tests/manyloc/keineschweine/keineschweine.nim +++ b/tests/manyloc/keineschweine/keineschweine.nim @@ -5,7 +5,7 @@ import sg_gui, sg_assets, sound_buffer, enet_client when defined(profiler): import nimprof -{.deadCodeElim: on.} + type PPlayer* = ref TPlayer TPlayer* = object diff --git a/tests/manyloc/keineschweine/lib/sg_gui.nim b/tests/manyloc/keineschweine/lib/sg_gui.nim index ffc4e8215..b7448d9df 100644 --- a/tests/manyloc/keineschweine/lib/sg_gui.nim +++ b/tests/manyloc/keineschweine/lib/sg_gui.nim @@ -2,7 +2,7 @@ import sfml, sfml_colors, input_helpers, sg_packets from strutils import countlines -{.deadCodeElim: on.} + type PGuiContainer* = ref TGuiContainer TGuiContainer* = object of TObject diff --git a/tests/manyloc/keineschweine/server/nim.cfg b/tests/manyloc/keineschweine/server/nim.cfg index fdc45a8e1..211ad3003 100644 --- a/tests/manyloc/keineschweine/server/nim.cfg +++ b/tests/manyloc/keineschweine/server/nim.cfg @@ -1,5 +1,4 @@ debugger = off -deadCodeElim = on path = ".." path = "../genpacket" path = "../helpers" diff --git a/tests/manyloc/nake/nakefile.nim b/tests/manyloc/nake/nakefile.nim index 2055d7834..97af79a84 100644 --- a/tests/manyloc/nake/nakefile.nim +++ b/tests/manyloc/nake/nakefile.nim @@ -10,7 +10,7 @@ const ExeName = "keineschweine" ServerDefines = "-d:NoSFML -d:NoChipmunk" TestBuildDefines = "-d:escapeMenuTest -d:debugWeps -d:showFPS -d:moreNim -d:debugKeys -d:foo -d:recordMode --forceBuild" - ReleaseDefines = "-d:release --deadCodeElim:on" + ReleaseDefines = "-d:release" ReleaseTestDefines = "-d:debugWeps -d:debugKeys --forceBuild" task "testprofile", "..": diff --git a/tests/manyloc/standalone/barebone.nim.cfg b/tests/manyloc/standalone/barebone.nim.cfg index bb350ff55..b956bef8e 100644 --- a/tests/manyloc/standalone/barebone.nim.cfg +++ b/tests/manyloc/standalone/barebone.nim.cfg @@ -1,3 +1,2 @@ --os:standalone ---deadCodeElim:on --gc:none diff --git a/tests/pragmas/tnoreturn.nim b/tests/pragmas/tnoreturn.nim index 4d00c6034..bb59b1c71 100644 --- a/tests/pragmas/tnoreturn.nim +++ b/tests/pragmas/tnoreturn.nim @@ -2,13 +2,16 @@ discard """ ccodeCheck: "\\i @'__attribute__((noreturn))' .*" """ -proc noret1*(i: int) {.noreturn.} = +proc noret1*(i: int) {.noreturn.} = echo i -proc noret2*(i: int): void {.noreturn.} = +proc noret2*(i: int): void {.noreturn.} = echo i +noret1(1) +noret2(2) + var p {.used.}: proc(i: int): int doAssert(not compiles( p = proc(i: int): int {.noreturn.} = i # noreturn lambda returns int diff --git a/tests/rodfiles/deadg.nim b/tests/rodfiles/deadg.nim index 587608e14..0aee59bb8 100644 --- a/tests/rodfiles/deadg.nim +++ b/tests/rodfiles/deadg.nim @@ -1,6 +1,3 @@ - -{.deadCodeElim: on.} - proc p1*(x, y: int): int = result = x + y diff --git a/tools/nim.bash-completion b/tools/nim.bash-completion index 1c199a0cf..8e569079a 100644 --- a/tools/nim.bash-completion +++ b/tools/nim.bash-completion @@ -15,7 +15,7 @@ _nim() return 0 fi case $prev in - --stackTrace|--lineTrace|--threads|-x|--checks|--objChecks|--fieldChecks|--rangeChecks|--boundChecks|--overflowChecks|-a|--assertions|--floatChecks|--nanChecks|--infChecks|--deadCodeElim) + --stackTrace|--lineTrace|--threads|-x|--checks|--objChecks|--fieldChecks|--rangeChecks|--boundChecks|--overflowChecks|-a|--assertions|--floatChecks|--nanChecks|--infChecks) # Options that require on/off [[ "$cur" == "=" ]] && cur="" COMPREPLY=( $(compgen -W 'on off' -- "$cur") ) @@ -32,7 +32,7 @@ _nim() return 0 ;; *) - kw="-r -p= --path= -d= --define= -u= --undef= -f --forceBuild --opt= --app= --stackTrace= --lineTrace= --threads= -x= --checks= --objChecks= --fieldChecks= --rangeChecks= --boundChecks= --overflowChecks= -a= --assertions= --floatChecks= --nanChecks= --infChecks= --deadCodeElim=" + kw="-r -p= --path= -d= --define= -u= --undef= -f --forceBuild --opt= --app= --stackTrace= --lineTrace= --threads= -x= --checks= --objChecks= --fieldChecks= --rangeChecks= --boundChecks= --overflowChecks= -a= --assertions= --floatChecks= --nanChecks= --infChecks=" COMPREPLY=( $( compgen -W "${kw}" -- $cur ) ) _filedir '@(nim)' #$split diff --git a/tools/nim.zsh-completion b/tools/nim.zsh-completion index 57b5e163e..e9b9002fb 100644 --- a/tools/nim.zsh-completion +++ b/tools/nim.zsh-completion @@ -60,8 +60,6 @@ _nim() { '*--infChecks=off[turn Inf checks off]' \ '*--nilChecks=on[turn nil checks on]' \ '*--nilChecks=off[turn nil checks off]' \ - '*--deadCodeElim=on[whole program dead code elimination on]' \ - '*--deadCodeElim=off[whole program dead code elimination off]' \ '*--opt=none[do not optimize]' \ '*--opt=speed[optimize for speed|size - use -d:release for a release build]' \ '*--opt=size[optimize for size]' \ -- cgit 1.4.1-2-gfad0 From ce634909281ffc8efbc7d192f557ffe38f49e740 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Fri, 4 May 2018 15:23:47 +0300 Subject: Yield in try --- compiler/closureiters.nim | 586 ++++++++++++++++++++++++++++++++++++++------- compiler/semexprs.nim | 2 +- lib/system/embedded.nim | 3 + lib/system/excpt.nim | 4 + tests/async/tasynctry2.nim | 4 +- tests/iter/tyieldintry.nim | 201 ++++++++++++++++ 6 files changed, 709 insertions(+), 91 deletions(-) create mode 100644 tests/iter/tyieldintry.nim (limited to 'tests/iter') diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 7653176de..504f70347 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -59,6 +59,80 @@ # if :tmpSlLower == 2: # yield 3 +# nkTryStmt Transformations: +# If the iter has an nkTryStmt with a yield inside +# - the closure iter is promoted to have exceptions (ctx.hasExceptions = true) +# - exception table is created. This is a const array, where +# `abs(exceptionTable[i])` is a state idx to which we should jump from state +# `i` should exception be raised in state `i`. For all states in `try` block +# the target state is `except` block. For all states in `except` block +# the target state is `finally` block. For all other states there is no +# target state (0, as the first block can never be neither except nor finally). +# `exceptionTable[i]` is < 0 if `abs(exceptionTable[i])` is except block, +# and > 0, for finally block. +# - local variable :curExc is created +# - the iter body is wrapped into a +# try: +# closureIterSetupExc(:curExc) +# ...body... +# catch: +# :state = exceptionTable[:state] +# if :state == 0: raise # No state that could handle exception +# :unrollFinally = :state > 0 # Target state is finally +# if :state < 0: +# :state = -:state +# :curExc = getCurrentException() +# +# nkReturnStmt within a try/except/finally now has to behave differently as we +# want the nearest finally block to be executed before the return, thus it is +# transformed to: +# :tmpResult = returnValue (if return doesn't have a value, this is skipped) +# :unrollFinally = true +# goto nearestFinally (or -1 if not exists) +# +# Every finally block calls closureIterEndFinally() upon its successful +# completion. +# +# Example: +# +# try: +# yield 0 +# raise ... +# except: +# yield 1 +# return 3 +# finally: +# yield 2 +# +# Is transformed to (yields are left in place for example simplicity, +# in reality the code is subdivided even more, as described above): +# +# STATE0: # Try +# yield 0 +# raise ... +# :state = 2 # What would happen should we not raise +# break :stateLoop +# STATE1: # Except +# yield 1 +# :tmpResult = 3 # Return +# :unrollFinally = true # Return +# :state = 2 # Goto Finally +# break :stateLoop +# :state = 2 # What would happen should we not return +# break :stateLoop +# STATE2: # Finally +# yield 2 +# if :unrollFinally: # This node is created by `newEndFinallyNode` +# when nearestFinally == 0: # Pseudocode. The `when` is not emitted in reality +# if :curExc.isNil: +# return :tmpResult +# else: +# raise +# else: +# :state = nearestFinally +# break :stateLoop +# state = -1 # Goto next state. In this case we just exit +# break :stateLoop import intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os, options, @@ -69,6 +143,10 @@ type Ctx = object 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 + states: seq[PNode] # The resulting states. Every state is an nkState node. blockLevel: int # Temp used to transform break and continue stmts stateLoopLabel: PSym # Label to break on, when jumping between states. @@ -76,20 +154,66 @@ type tempVarId: int # unique name counter tempVars: PNode # Temp var decls, nkVarSection loweredStmtListExpr: PNode # Temporary used for nkStmtListExpr lowering + exceptionTable: seq[int] # For state `i` jump to state `exceptionTable[i]` if exception is raised + hasExceptions: bool # Does closure have yield in try? + curExcHandlingState: int # Negative for except, positive for finally + nearestFinally: int # Index of the nearest finally block. For try/except it + # is their finally. For finally it is parent finally. Otherwise -1 + +proc newStateAccess(ctx: var Ctx): PNode = + if ctx.stateVarSym.isNil: + result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)), getStateField(ctx.fn), ctx.fn.info) + else: + result = newSymNode(ctx.stateVarSym) + +proc newStateAssgn(ctx: var Ctx, toValue: PNode): PNode = + # Creates state assignment: + # :state = toValue + result = newNode(nkAsgn) + result.add(ctx.newStateAccess()) + result.add(toValue) proc newStateAssgn(ctx: var Ctx, stateNo: int = -2): PNode = - # Creates state assignmen: + # Creates state assignment: # :state = stateNo + ctx.newStateAssgn(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt))) - result = newNode(nkAsgn) +proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym = + result = newSym(skVar, getIdent(name), ctx.fn, ctx.fn.info) + result.typ = typ + + if not ctx.stateVarSym.isNil: + # We haven't gone through labmda lifting yet, so just create a local var, + # it will be lifted later + if ctx.tempVars.isNil: + ctx.tempVars = newNode(nkVarSection) + addVar(ctx.tempVars, newSymNode(result)) + else: + let envParam = getEnvParam(ctx.fn) + # let obj = envParam.typ.lastSon + result = addUniqueField(envParam.typ.lastSon, result) + +proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode = if ctx.stateVarSym.isNil: - let state = getStateField(ctx.fn) - assert state != nil - result.add(rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)), - state, result.info)) + result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)), s, ctx.fn.info) else: - result.add(newSymNode(ctx.stateVarSym)) - result.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt))) + result = newSymNode(s) + +proc newTmpResultAccess(ctx: var Ctx): PNode = + if ctx.tmpResultSym.isNil: + debug(ctx.fn.typ) + ctx.tmpResultSym = ctx.newEnvVar(":tmpResult", ctx.fn.typ[0]) + ctx.newEnvVarAccess(ctx.tmpResultSym) + +proc newUnrollFinallyAccess(ctx: var Ctx): PNode = + if ctx.unrollFinallySym.isNil: + ctx.unrollFinallySym = ctx.newEnvVar(":unrollFinally", getSysType(tyBool)) + ctx.newEnvVarAccess(ctx.unrollFinallySym) + +proc newCurExcAccess(ctx: var Ctx): PNode = + if ctx.curExcSym.isNil: + ctx.curExcSym = ctx.newEnvVar(":curExc", callCodegenProc("getCurrentException", emptyNode).typ) + ctx.newEnvVarAccess(ctx.curExcSym) proc setStateInAssgn(stateAssgn: PNode, stateNo: int) = assert stateAssgn.kind == nkAsgn @@ -107,6 +231,8 @@ proc newState(ctx: var Ctx, n, gotoOut: PNode): int = s.add(resLit) s.add(n) ctx.states.add(s) + ctx.exceptionTable.add(ctx.curExcHandlingState) + if not gotoOut.isNil: assert(gotoOut.len == 0) gotoOut.add(newIntLit(result)) @@ -119,27 +245,13 @@ proc toStmtList(n: PNode): PNode = proc addGotoOut(n: PNode, gotoOut: PNode): PNode = # Make sure `n` is a stmtlist, and ends with `gotoOut` - result = toStmtList(n) if result.len != 0 and result.sons[^1].kind != nkGotoState: result.add(gotoOut) proc newTempVarAccess(ctx: var Ctx, typ: PType, i: TLineInfo): PNode = - let s = newSym(skVar, getIdent(":tmpSlLower" & $ctx.tempVarId), ctx.fn, i) - s.typ = typ - - if not ctx.stateVarSym.isNil: - # We haven't gone through labmda lifting yet, so just create a local var, - # it will be lifted later - if ctx.tempVars.isNil: - ctx.tempVars = newNode(nkVarSection) - addVar(ctx.tempVars, newSymNode(s)) - - result = newSymNode(s) - else: - # Lambda lifting is done, insert temp var to env. - result = freshVarForClosureIter(s, ctx.fn) - + let s = ctx.newEnvVar(":tmpSlLower" & $ctx.tempVarId, typ) + result = ctx.newEnvVarAccess(s) inc ctx.tempVarId proc hasYields(n: PNode): bool = @@ -197,7 +309,17 @@ proc transformBreaksInBlock(ctx: var Ctx, n: PNode, label, after: PNode): PNode for i in 0 ..< n.len: n[i] = ctx.transformBreaksInBlock(n[i], label, after) -proc collectExceptState(n: PNode): PNode = +proc newNullifyCurExc(ctx: var Ctx): PNode = + # :curEcx = nil + result = newNode(nkAsgn) + let curExc = ctx.newCurExcAccess() + result.add(curExc) + + let nilnode = newNode(nkNilLit) + nilnode.typ = curExc.typ + result.add(nilnode) + +proc collectExceptState(ctx: var Ctx, n: PNode): PNode = var ifStmt = newNode(nkIfStmt) for c in n: if c.kind == nkExceptBranch: @@ -208,8 +330,10 @@ proc collectExceptState(n: PNode): PNode = assert(c.len == 2) ifBranch = newNode(nkElifBranch) let expression = newNodeI(nkCall, n.info) + expression.add(newSymNode(getSysMagic("of", mOf))) expression.add(callCodegenProc("getCurrentException", emptyNode)) expression.add(c[0]) + expression.typ = getSysType(tyBool) ifBranch.add(expression) branchBody = c[1] else: @@ -226,10 +350,37 @@ proc collectExceptState(n: PNode): PNode = if ifStmt.len != 0: result = newNode(nkStmtList) + result.add(ctx.newNullifyCurExc()) result.add(ifStmt) else: result = emptyNode +proc addElseToExcept(ctx: var Ctx, n: PNode) = + if n.kind == nkStmtList and n[1].kind == nkIfStmt and n[1][^1].kind != nkElse: + # Not all cases are covered + let elseBranch = newNode(nkElse) + let branchBody = newNode(nkStmtList) + + block: # :unrollFinally = true + let asgn = newNode(nkAsgn) + asgn.add(ctx.newUnrollFinallyAccess()) + asgn.add(newIntTypeNode(nkIntLit, 1, getSysType(tyBool))) + branchBody.add(asgn) + + block: # :curExc = getCurrentException() + let asgn = newNode(nkAsgn) + asgn.add(ctx.newCurExcAccess) + asgn.add(callCodegenProc("getCurrentException", emptyNode)) + branchBody.add(asgn) + + block: # goto nearestFinally + let goto = newNode(nkGotoState) + goto.add(newIntLit(ctx.nearestFinally)) + branchBody.add(goto) + + elseBranch.add(branchBody) + n[1].add(elseBranch) + proc getFinallyNode(n: PNode): PNode = result = n[^1] if result.kind == nkFinally: @@ -273,6 +424,101 @@ proc lowerStmtListExpr(ctx: var Ctx, n: PNode): PNode = for i in 0 ..< n.len: n[i] = ctx.lowerStmtListExpr(n[i]) +proc newEndFinallyNode(ctx: var Ctx): PNode = + # Generate the following code: + # if :unrollFinally: + # when nearestFinally == 0: # Pseudocode. The `when` is not emitted in reality + # if :curExc.isNil: + # return :tmpResult + # else: + # raise + # else: + # goto nearestFinally + # :state = nearestFinally + # break :stateLoop + + result = newNode(nkIfStmt) + + let elifBranch = newNode(nkElifBranch) + elifBranch.add(ctx.newUnrollFinallyAccess()) + result.add(elifBranch) + + var ifBody: PNode + + if ctx.nearestFinally == 0 or true: + ifBody = newNode(nkIfStmt) + let branch = newNode(nkElifBranch) + + let cmp = newNode(nkCall) + cmp.add(getSysMagic("==", mEqRef).newSymNode) + let curExc = ctx.newCurExcAccess() + let nilnode = newNode(nkNilLit) + nilnode.typ = curExc.typ + cmp.add(curExc) + cmp.add(nilnode) + cmp.typ = getSysType(tyBool) + branch.add(cmp) + + var retStmt = newNode(nkReturnStmt) + if true: + var a = newNode(nkAsgn) + addSon(a, newSymNode(getClosureIterResult(ctx.fn))) + addSon(a, ctx.newTmpResultAccess()) + retStmt.add(a) + else: + retStmt.add(emptyNode) + branch.add(retStmt) + + let elseBranch = newNode(nkElse) + let raiseStmt = newNode(nkRaiseStmt) + + # The C++ backend requires `getCurrentException` here. + raiseStmt.add(callCodegenProc("getCurrentException", emptyNode)) + elseBranch.add(raiseStmt) + + ifBody.add(branch) + ifBody.add(elseBranch) + else: + ifBody = newNode(nkGotoState) + ifBody.add(newIntLit(ctx.nearestFinally)) + + elifBranch.add(ifBody) + +proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode = + result = n + # TODO: This is very inefficient. It traverses the node, looking for nkYieldStmt. + case n.kind + of nkReturnStmt: + # We're somewhere in try, transform to finally unrolling + assert(ctx.nearestFinally != 0) + + result = newNodeI(nkStmtList, n.info) + + block: # :unrollFinally = true + let asgn = newNodeI(nkAsgn, n.info) + asgn.add(ctx.newUnrollFinallyAccess()) + asgn.add(newIntTypeNode(nkIntLit, 1, getSysType(tyBool))) + result.add(asgn) + + if n[0].kind != nkEmpty: # TODO: And not void! + let asgnTmpResult = newNodeI(nkAsgn, n.info) + asgnTmpResult.add(ctx.newTmpResultAccess()) + asgnTmpResult.add(n[0]) + result.add(asgnTmpResult) + + result.add(ctx.newNullifyCurExc()) + + let goto = newNodeI(nkGotoState, n.info) + goto.add(newIntLit(ctx.nearestFinally)) + result.add(goto) + + of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit, + nkSym, nkIdent, procDefs, nkTemplateDef: + discard + else: + for i in 0 ..< n.len: + n[i] = ctx.transformReturnsInTry(n[i]) + proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode = result = n case n.kind: @@ -310,7 +556,6 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode assert(false, "nkStmtListExpr not lowered") of nkYieldStmt: - # echo "YIELD!" result = newNodeI(nkStmtList, n.info) result.add(n) result.add(gotoOut) @@ -371,34 +616,64 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode result[1] = ctx.transformClosureIteratorBody(result[1], gotoOut) of nkTryStmt: - var tryBody = toStmtList(n[0]) + # See explanation above about how this works + ctx.hasExceptions = true - # let popTry = newNode(nkPar) - # popTry.add(newIdentNode(getIdent("popTry"), n.info)) + result = newNode(nkGotoState) + var tryBody = toStmtList(n[0]) + var exceptBody = ctx.collectExceptState(n) var finallyBody = newNode(nkStmtList) - # finallyBody.add(popTry) finallyBody.add(getFinallyNode(n)) + finallyBody = ctx.transformReturnsInTry(finallyBody) + finallyBody.add(ctx.newEndFinallyNode()) - var tryCatchOut = newNode(nkGotoState) + # The following index calculation is based on the knowledge how state + # indexes are assigned + let tryIdx = ctx.states.len + var exceptIdx, finallyIdx: int + if exceptBody.kind != nkEmpty: + exceptIdx = -(tryIdx + 1) + finallyIdx = tryIdx + 2 + else: + exceptIdx = tryIdx + 1 + finallyIdx = tryIdx + 1 - tryBody = ctx.transformClosureIteratorBody(tryBody, tryCatchOut) - var exceptBody = collectExceptState(n) + let outToFinally = newNode(nkGotoState) - var exceptIdx = -1 - if exceptBody.kind != nkEmpty: - exceptBody = ctx.transformClosureIteratorBody(exceptBody, tryCatchOut) - exceptIdx = ctx.newState(exceptBody, nil) + block: # Create initial states. + let oldExcHandlingState = ctx.curExcHandlingState + ctx.curExcHandlingState = exceptIdx + let realTryIdx = ctx.newState(tryBody, result) + assert(realTryIdx == tryIdx) + + if exceptBody.kind != nkEmpty: + ctx.curExcHandlingState = finallyIdx + let realExceptIdx = ctx.newState(exceptBody, nil) + assert(realExceptIdx == -exceptIdx) - finallyBody = ctx.transformClosureIteratorBody(finallyBody, gotoOut) - let finallyIdx = ctx.newState(finallyBody, tryCatchOut) + ctx.curExcHandlingState = oldExcHandlingState + let realFinallyIdx = ctx.newState(finallyBody, outToFinally) + assert(realFinallyIdx == finallyIdx) - # let pushTry = newNode(nkPar) #newCall(newSym("pushTry"), newIntLit(exceptIdx)) - # pushTry.add(newIdentNode(getIdent("pushTry"), n.info)) - # pushTry.add(newIntLit(exceptIdx)) - # pushTry.add(newIntLit(finallyIdx)) - # tryBody.sons.insert(pushTry, 0) + block: # Subdivide the states + let oldNearestFinally = ctx.nearestFinally + ctx.nearestFinally = finallyIdx - result = tryBody + let oldExcHandlingState = ctx.curExcHandlingState + + ctx.curExcHandlingState = exceptIdx + + discard ctx.transformReturnsInTry(tryBody) + discard ctx.transformClosureIteratorBody(tryBody, outToFinally) + + ctx.curExcHandlingState = finallyIdx + ctx.addElseToExcept(exceptBody) + discard ctx.transformReturnsInTry(exceptBody) + discard ctx.transformClosureIteratorBody(exceptBody, outToFinally) + + ctx.curExcHandlingState = oldExcHandlingState + ctx.nearestFinally = oldNearestFinally + discard ctx.transformClosureIteratorBody(finallyBody, gotoOut) of nkGotoState, nkForStmt: internalError("closure iter " & $n.kind) @@ -460,7 +735,6 @@ proc tranformStateAssignments(ctx: var Ctx, n: PNode): PNode = discard of nkReturnStmt: - result = newNodeI(nkStmtList, n.info) result.add(ctx.newStateAssgn(-1)) result.add(n) @@ -483,6 +757,29 @@ proc skipStmtList(n: PNode): PNode = if result.len == 0: return emptyNode result = result[0] +proc skipEmptyStates(ctx: Ctx, stateIdx: int): int = + # Returns first non-empty state idx for `stateIdx`. Returns `stateIdx` if + # it is not empty + var maxJumps = ctx.states.len # maxJumps used only for debugging purposes. + var stateIdx = stateIdx + while true: + let label = stateIdx + if label == ctx.exitStateIdx: break + var newLabel = label + if label == -1: + newLabel = ctx.exitStateIdx + else: + let fs = ctx.states[label][1].skipStmtList() + if fs.kind == nkGotoState: + newLabel = fs[0].intVal.int + if label == newLabel: break + stateIdx = newLabel + dec maxJumps + if maxJumps == 0: + assert(false, "Internal error") + + result = ctx.states[stateIdx][0].intVal.int + proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode = result = n case n.kind @@ -490,31 +787,143 @@ proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode = nkSym, nkIdent, procDefs, nkTemplateDef: discard of nkGotoState: - var maxJumps = ctx.states.len # maxJumps used only for debugging purposes. result = copyTree(n) - while true: - let label = result[0].intVal.int - if label == ctx.exitStateIdx: break - var newLabel = label - if label == -1: - newLabel = ctx.exitStateIdx - else: - let fs = ctx.states[label][1].skipStmtList() - if fs.kind == nkGotoState: - newLabel = fs[0].intVal.int - if label == newLabel: break - result[0].intVal = newLabel - dec maxJumps - if maxJumps == 0: - assert(false, "Internal error") - - let label = result[0].intVal.int - result[0].intVal = ctx.states[label][0].intVal + result[0].intVal = ctx.skipEmptyStates(result[0].intVal.int) else: for i in 0 ..< n.len: n[i] = ctx.skipThroughEmptyStates(n[i]) +proc newArrayType(n: int, t: PType, owner: PSym): PType = + result = newType(tyArray, owner) + + let rng = newType(tyRange, owner) + rng.n = newNode(nkRange) + rng.n.add(newIntLit(0)) + rng.n.add(newIntLit(n)) + rng.rawAddSon(t) + + result.rawAddSon(rng) + result.rawAddSon(t) + +proc createExceptionTable(ctx: var Ctx): PNode = + result = newNode(nkBracket) + result.typ = newArrayType(ctx.exceptionTable.len, getSysType(tyInt16), ctx.fn) + + for i in ctx.exceptionTable: + let elem = newIntNode(nkIntLit, i) + elem.typ = getSysType(tyInt16) + result.add(elem) + +proc newCatchBody(ctx: var Ctx): PNode {.inline.} = + # Generates the code: + # :state = exceptionTable[:state] + # if :state == 0: raise + # :unrollFinally = :state > 0 + # if :state < 0: + # :state = -:state + # :curExc = getCurrentException() + + result = newNode(nkStmtList) + + # :state = exceptionTable[:state] + block: + + # exceptionTable[:state] + let getNextState = newNode(nkBracketExpr) + getNextState.add(ctx.createExceptionTable) + getNextState.add(ctx.newStateAccess()) + getNextState.typ = getSysType(tyInt) + + # :state = exceptionTable[:state] + result.add(ctx.newStateAssgn(getNextState)) + + # if :state == 0: raise + block: + let ifStmt = newNode(nkIfStmt) + let ifBranch = newNode(nkElifBranch) + let cond = newNode(nkCall) + cond.add(getSysMagic("==", mEqI).newSymNode) + cond.add(ctx.newStateAccess()) + cond.add(newIntTypeNode(nkIntLit, 0, getSysType(tyInt))) + cond.typ = getSysType(tyBool) + ifBranch.add(cond) + + let raiseStmt = newNode(nkRaiseStmt) + raiseStmt.add(emptyNode) + + ifBranch.add(raiseStmt) + ifStmt.add(ifBranch) + result.add(ifStmt) + + # :unrollFinally = :state > 0 + block: + let asgn = newNode(nkAsgn) + asgn.add(ctx.newUnrollFinallyAccess()) + + let cond = newNode(nkCall) + cond.add(getSysMagic("<", mLtI).newSymNode) + cond.add(newIntTypeNode(nkIntLit, 0, getSysType(tyInt))) + cond.add(ctx.newStateAccess()) + cond.typ = getSysType(tyBool) + asgn.add(cond) + result.add(asgn) + + # if :state < 0: :state = -:state + block: + let ifStmt = newNode(nkIfStmt) + let ifBranch = newNode(nkElifBranch) + let cond = newNode(nkCall) + cond.add(getSysMagic("<", mLtI).newSymNode) + cond.add(ctx.newStateAccess()) + cond.add(newIntTypeNode(nkIntLit, 0, getSysType(tyInt))) + cond.typ = getSysType(tyBool) + ifBranch.add(cond) + + let negateState = newNode(nkCall) + negateState.add(getSysMagic("-", mUnaryMinusI).newSymNode) + negateState.add(ctx.newStateAccess()) + negateState.typ = getSysType(tyInt) + + ifBranch.add(ctx.newStateAssgn(negateState)) + ifStmt.add(ifBranch) + result.add(ifStmt) + + # :curExc = getCurrentException() + block: + let getCurExc = callCodegenProc("getCurrentException", emptyNode) + let asgn = newNode(nkAsgn) + asgn.add(ctx.newCurExcAccess()) + asgn.add(getCurExc) + result.add(asgn) + +proc wrapIntoTryExcept(ctx: var Ctx, n: PNode): PNode = + result = newNode(nkTryStmt) + + let tryBody = newNode(nkStmtList) + + let setupExc = newNode(nkCall) + setupExc.add(newSymNode(getCompilerProc("closureIterSetupExc"))) + + tryBody.add(setupExc) + + tryBody.add(n) + result.add(tryBody) + + let catchNode = newNode(nkExceptBranch) + result.add(catchNode) + + let catchBody = newNode(nkStmtList) + catchBody.add(ctx.newCatchBody()) + catchNode.add(catchBody) + + setupExc.add(ctx.newCurExcAccess()) + proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode = + # while true: + # block :stateLoop: + # gotoState :state + # body # Might get wrapped in try-except + result = newNode(nkWhileStmt) result.add(newSymNode(getSysSym("true"))) @@ -532,19 +941,19 @@ proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode = let blockStmt = newNodeI(nkBlockStmt, n.info) blockStmt.add(newSymNode(ctx.stateLoopLabel)) - let blockBody = newNodeI(nkStmtList, n.info) - blockStmt.add(blockBody) + var blockBody = newNodeI(nkStmtList, n.info) let gs = newNodeI(nkGotoState, n.info) - if ctx.stateVarSym.isNil: - gs.add(rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)), getStateField(ctx.fn), n.info)) - else: - gs.add(newSymNode(ctx.stateVarSym)) - + gs.add(ctx.newStateAccess()) gs.add(newIntLit(ctx.states.len - 1)) + blockBody.add(gs) blockBody.add(n) - # gs.add(rawIndirectAccess(newSymNode(ctx.fn.getHiddenParam), getStateField(ctx.fn), n.info)) + + if ctx.hasExceptions: + blockBody = ctx.wrapIntoTryExcept(blockBody) + + blockStmt.add(blockBody) loopBody.add(blockStmt) @@ -558,7 +967,7 @@ proc deleteEmptyStates(ctx: var Ctx) = var iValid = 0 for i, s in ctx.states: let body = s[1].skipStmtList() - if body.kind == nkGotoState and i != ctx.states.len - 1: + if body.kind == nkGotoState and i != ctx.states.len - 1 and i != 0: # This is an empty state. Mark with -1. s[0].intVal = -1 else: @@ -567,14 +976,20 @@ proc deleteEmptyStates(ctx: var Ctx) = for i, s in ctx.states: let body = s[1].skipStmtList() - if body.kind != nkGotoState: + if body.kind != nkGotoState or i == 0: discard ctx.skipThroughEmptyStates(s) + let excHandlState = ctx.exceptionTable[i] + if excHandlState < 0: + ctx.exceptionTable[i] = -ctx.skipEmptyStates(-excHandlState) + elif excHandlState != 0: + ctx.exceptionTable[i] = ctx.skipEmptyStates(excHandlState) var i = 0 while i < ctx.states.len - 1: let fs = ctx.states[i][1].skipStmtList() - if fs.kind == nkGotoState: + if fs.kind == nkGotoState and i != 0: ctx.states.delete(i) + ctx.exceptionTable.delete(i) else: inc i @@ -591,6 +1006,7 @@ proc transformClosureIterator*(fn: PSym, n: PNode): PNode = ctx.states = @[] ctx.stateLoopLabel = newSym(skLabel, getIdent(":stateLoop"), fn, fn.info) + ctx.exceptionTable = @[] let n = n.toStmtList discard ctx.newState(n, nil) @@ -613,19 +1029,11 @@ proc transformClosureIterator*(fn: PSym, n: PNode): PNode = result.add(body) result = ctx.tranformStateAssignments(result) - - # Add excpetion handling - var hasExceptions = false - if hasExceptions: - discard # TODO: - # result = wrapIntoTryCatch(result) - - # while true: - # block :stateLoop: - # gotoState - # body result = ctx.wrapIntoStateLoop(result) - # echo "TRANSFORM TO STATES2: " - # debug(result) + # echo "TRANSFORM TO STATES: " # echo renderTree(result) + + # echo "exception table:" + # for i, e in ctx.exceptionTable: + # echo i, " -> ", e diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 1ef284a77..79010bfde 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1544,7 +1544,7 @@ proc semYield(c: PContext, n: PNode): PNode = checkSonsLen(n, 1) if c.p.owner == nil or c.p.owner.kind != skIterator: localError(n.info, errYieldNotAllowedHere) - elif c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline: + elif oldIterTransf in c.features and c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline: localError(n.info, errYieldNotAllowedInTryStmt) elif n.sons[0].kind != nkEmpty: n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility: diff --git a/lib/system/embedded.nim b/lib/system/embedded.nim index 46e84e056..4d453fcca 100644 --- a/lib/system/embedded.nim +++ b/lib/system/embedded.nim @@ -41,3 +41,6 @@ proc reraiseException() {.compilerRtl.} = proc writeStackTrace() = discard proc setControlCHook(hook: proc () {.noconv.}) = discard + +proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} = + sysFatal(ReraiseError, "exception handling is not available") diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index fb38948f7..dabfe010e 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -131,6 +131,10 @@ proc popCurrentExceptionEx(id: uint) {.compilerRtl.} = quitOrDebug() prev.up = cur.up +proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} = + if not e.isNil: + currException = e + # some platforms have native support for stack traces: const nativeStackTraceSupported* = (defined(macosx) or defined(linux)) and diff --git a/tests/async/tasynctry2.nim b/tests/async/tasynctry2.nim index 444a058be..f82b6cfe0 100644 --- a/tests/async/tasynctry2.nim +++ b/tests/async/tasynctry2.nim @@ -1,10 +1,12 @@ discard """ file: "tasynctry2.nim" errormsg: "\'yield\' cannot be used within \'try\' in a non-inlined iterator" - line: 15 + line: 17 """ import asyncdispatch +{.experimental: "oldIterTransf".} + proc foo(): Future[bool] {.async.} = discard proc test5(): Future[int] {.async.} = diff --git a/tests/iter/tyieldintry.nim b/tests/iter/tyieldintry.nim new file mode 100644 index 000000000..9cb199c5b --- /dev/null +++ b/tests/iter/tyieldintry.nim @@ -0,0 +1,201 @@ +discard """ +targets: "c cpp" +output: "ok" +""" +var closureIterResult = newSeq[int]() + +proc checkpoint(arg: int) = + closureIterResult.add(arg) + +type + TestException = object of Exception + AnotherException = object of Exception + +proc testClosureIterAux(it: iterator(): int, exceptionExpected: bool, expectedResults: varargs[int]) = + closureIterResult.setLen(0) + + var exceptionCaught = false + + try: + for i in it(): + closureIterResult.add(i) + except TestException: + exceptionCaught = true + + if closureIterResult != @expectedResults or exceptionCaught != exceptionExpected: + if closureIterResult != @expectedResults: + echo "Expected: ", @expectedResults + echo "Actual: ", closureIterResult + if exceptionCaught != exceptionExpected: + echo "Expected exception: ", exceptionExpected + echo "Got exception: ", exceptionCaught + doAssert(false) + +proc test(it: iterator(): int, expectedResults: varargs[int]) = + testClosureIterAux(it, false, expectedResults) + +proc testExc(it: iterator(): int, expectedResults: varargs[int]) = + testClosureIterAux(it, true, expectedResults) + +proc raiseException() = + raise newException(TestException, "Test exception!") + +block: + iterator it(): int {.closure.} = + var i = 5 + while i != 0: + yield i + if i == 3: + yield 123 + dec i + + test(it, 5, 4, 3, 123, 2, 1) + +block: + iterator it(): int {.closure.} = + yield 0 + try: + checkpoint(1) + raiseException() + except TestException: + checkpoint(2) + yield 3 + checkpoint(4) + finally: + checkpoint(5) + + checkpoint(6) + + test(it, 0, 1, 2, 3, 4, 5, 6) + +block: + iterator it(): int {.closure.} = + yield 0 + try: + yield 1 + checkpoint(2) + finally: + checkpoint(3) + yield 4 + checkpoint(5) + yield 6 + + test(it, 0, 1, 2, 3, 4, 5, 6) + +block: + iterator it(): int {.closure.} = + yield 0 + try: + yield 1 + raiseException() + yield 2 + finally: + checkpoint(3) + yield 4 + checkpoint(5) + yield 6 + + testExc(it, 0, 1, 3, 4, 5, 6) + +block: + iterator it(): int {.closure.} = + try: + try: + raiseException() + except AnotherException: + yield 123 + finally: + checkpoint(3) + finally: + checkpoint(4) + + testExc(it, 3, 4) + +block: + iterator it(): int {.closure.} = + try: + yield 1 + raiseException() + except AnotherException: + checkpoint(123) + finally: + checkpoint(2) + checkpoint(3) + + testExc(it, 1, 2) + +block: + iterator it(): int {.closure.} = + try: + yield 0 + try: + yield 1 + try: + yield 2 + raiseException() + except AnotherException: + yield 123 + finally: + yield 3 + except AnotherException: + yield 124 + finally: + yield 4 + checkpoint(1234) + except: + yield 5 + checkpoint(6) + finally: + checkpoint(7) + yield 8 + checkpoint(9) + + test(it, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) + +block: + iterator it(): int {.closure.} = + try: + yield 0 + return 2 + finally: + checkpoint(1) + checkpoint(123) + + test(it, 0, 1) + +block: + iterator it(): int {.closure.} = + try: + try: + yield 0 + raiseException() + finally: + checkpoint(1) + except TestException: + yield 2 + return + finally: + yield 3 + + checkpoint(123) + + test(it, 0, 1, 2, 3) + +block: + iterator it(): int {.closure.} = + try: + try: + yield 0 + raiseException() + finally: + return # Return in finally should stop exception propagation + except AnotherException: + yield 2 + return + finally: + yield 3 + checkpoint(123) + + test(it, 0, 3) + +echo "ok" -- cgit 1.4.1-2-gfad0 From 14ca79fe1f1fafb8e3aff2e4c27bcb94c0595792 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Tue, 8 May 2018 01:25:08 +0300 Subject: More elaborate nkStmtListExpr lowering --- compiler/closureiters.nim | 404 +++++++++++++++++++++++++++++++++++---- tests/iter/tyieldintry.nim | 459 +++++++++++++++++++++++++++++++-------------- 2 files changed, 687 insertions(+), 176 deletions(-) (limited to 'tests/iter') diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index a8e7e0274..a30b4e10e 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -149,7 +149,6 @@ type exitStateIdx: int # index of the last state tempVarId: int # unique name counter tempVars: PNode # Temp var decls, nkVarSection - loweredStmtListExpr: PNode # Temporary used for nkStmtListExpr lowering exceptionTable: seq[int] # For state `i` jump to state `exceptionTable[i]` if exception is raised hasExceptions: bool # Does closure have yield in try? curExcHandlingState: int # Negative for except, positive for finally @@ -177,6 +176,7 @@ proc newStateAssgn(ctx: var Ctx, stateNo: int = -2): PNode = proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym = result = newSym(skVar, getIdent(name), ctx.fn, ctx.fn.info) result.typ = typ + assert(not typ.isNil) if not ctx.stateVarSym.isNil: # We haven't gone through labmda lifting yet, so just create a local var, @@ -197,7 +197,6 @@ proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode = proc newTmpResultAccess(ctx: var Ctx): PNode = if ctx.tmpResultSym.isNil: - debug(ctx.fn.typ) ctx.tmpResultSym = ctx.newEnvVar(":tmpResult", ctx.fn.typ[0]) ctx.newEnvVarAccess(ctx.tmpResultSym) @@ -245,9 +244,8 @@ proc addGotoOut(n: PNode, gotoOut: PNode): PNode = if result.len != 0 and result.sons[^1].kind != nkGotoState: result.add(gotoOut) -proc newTempVarAccess(ctx: var Ctx, typ: PType, i: TLineInfo): PNode = - let s = ctx.newEnvVar(":tmpSlLower" & $ctx.tempVarId, typ) - result = ctx.newEnvVarAccess(s) +proc newTempVar(ctx: var Ctx, typ: PType): PSym = + result = ctx.newEnvVar(":tmpSlLower" & $ctx.tempVarId, typ) inc ctx.tempVarId proc hasYields(n: PNode): bool = @@ -390,35 +388,382 @@ proc hasYieldsInExpressions(n: PNode): bool = nkSym, nkIdent, procDefs, nkTemplateDef: discard of nkStmtListExpr: - result = n.hasYields - of nkStmtList, nkWhileStmt, nkCaseStmt, nkIfStmt: - discard + if isEmptyType(n.typ): + for c in n: + if c.hasYieldsInExpressions: + return true + else: + result = n.hasYields else: for c in n: if c.hasYieldsInExpressions: return true -proc lowerStmtListExpr(ctx: var Ctx, n: PNode): PNode = +proc exprToStmtList(n: PNode): tuple[s, res: PNode] = + assert(n.kind == nkStmtListExpr) + + var parent = n + var lastSon = n[^1] + + while lastSon.kind == nkStmtListExpr: + parent = lastSon + lastSon = lastSon[^1] + + result.s = newNodeI(nkStmtList, n.info) + result.s.sons = parent.sons + result.s.sons.setLen(result.s.sons.len - 1) # delete last son + result.res = lastSon + +proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode = + result = newNode(nkFastAsgn) + result.add(ctx.newEnvVarAccess(s)) + result.add(v) + +proc addExprAssgn(ctx: Ctx, output, input: PNode, sym: PSym) = + if input.kind == nkStmtListExpr: + let (st, res) = exprToStmtList(input) + output.add(st) + output.add(ctx.newEnvVarAsgn(sym, res)) + else: + output.add(ctx.newEnvVarAsgn(sym, input)) + +proc convertExprBodyToAsgn(ctx: Ctx, exprBody: PNode, res: PSym): PNode = + result = newNode(nkStmtList) + ctx.addExprAssgn(result, exprBody, res) + +proc newNotCall(e: PNode): PNode = + result = newNode(nkCall) + result.add(newSymNode(getSysMagic("not", mNot))) + result.add(e) + result.typ = getSysType(tyBool) + +proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = result = n case n.kind of nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkStrLit..nkTripleStrLit, nkSym, nkIdent, procDefs, nkTemplateDef: discard - of nkStmtListExpr: - if n.hasYields: - for i in 0 .. n.len - 2: - ctx.loweredStmtListExpr.add(n[i]) - let tv = ctx.newTempVarAccess(n.typ, n[^1].info) - let asgn = newNode(nkAsgn) - asgn.add(tv) - asgn.add(n[^1]) - ctx.loweredStmtListExpr.add(asgn) - result = tv + of nkYieldStmt: + var ns = false + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], ns) + + if ns: + assert(n[0].kind == nkStmtListExpr) + result = newNodeI(nkStmtList, n.info) + let (st, ex) = exprToStmtList(n[0]) + result.add(st) + n[0] = ex + result.add(n) + + needsSplit = true + + of nkPar, nkObjConstr, nkTupleConstr, nkBracket, nkArgList: + var ns = false + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], ns) + + if ns: + needsSplit = true + + result = newNodeI(nkStmtListExpr, n.info) + if n.typ.isNil: internalError("lowerStmtListExprs: constr typ.isNil") + result.typ = n.typ + + for i in 0 ..< n.len: + if n[i].kind == nkStmtListExpr: + let (st, ex) = exprToStmtList(n[i]) + result.add(st) + n[i] = ex + result.add(n) + + of nkIfStmt, nkIfExpr: + var ns = false + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], ns) + + if ns: + needsSplit = true + var tmp: PSym + var s: PNode + let isExpr = not isEmptyType(n.typ) + if isExpr: + tmp = ctx.newTempVar(n.typ) + result = newNode(nkStmtListExpr) + result.typ = n.typ + else: + result = newNode(nkStmtList) + + var curS = result + + for branch in n: + case branch.kind + of nkElseExpr, nkElse: + if isExpr: + var newBranch = newNodeI(nkElse, branch.info) + let branchBody = newNode(nkStmtList) + ctx.addExprAssgn(branchBody, branch[0], tmp) + newBranch.add(branchBody) + curS.add(newBranch) + else: + curS.add(branch) + + of nkElifExpr, nkElifBranch: + var newBranch: PNode + if branch[0].kind == nkStmtListExpr: + let elseBody = newNode(nkStmtList) + + let (st, res) = exprToStmtList(branch[0]) + elseBody.add(st) + + newBranch = newNodeI(nkElifBranch, branch.info) + newBranch.add(res) + newBranch.add(branch[1]) + + let newIf = newNodeI(nkIfStmt, branch.info) + newIf.add(newBranch) + elseBody.add(newIf) + if curS.kind == nkIfStmt: + let newElse = newNodeI(nkElse, branch.info) + newElse.add(elseBody) + curS.add(newElse) + else: + curS.add(elseBody) + curS = newIf + else: + newBranch = branch + if curS.kind == nkIfStmt: + curS.add(newBranch) + else: + let newIf = newNodeI(nkIfStmt, branch.info) + newIf.add(newBranch) + curS.add(newIf) + curS = newIf + + if isExpr: + let branchBody = newNode(nkStmtList) + ctx.addExprAssgn(branchBody, branch[1], tmp) + newBranch[1] = branchBody + + else: + internalError("lowerStmtListExpr(nkIf): " & $branch.kind) + + if isExpr: result.add(ctx.newEnvVarAccess(tmp)) + + of nkTryStmt: + var ns = false + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], ns) + + if ns: + needsSplit = true + let isExpr = not isEmptyType(n.typ) + + if isExpr: + result = newNodeI(nkStmtListExpr, n.info) + result.typ = n.typ + let tmp = ctx.newTempVar(n.typ) + + n[0] = ctx.convertExprBodyToAsgn(n[0], tmp) + for i in 1 ..< n.len: + let branch = n[i] + case branch.kind + of nkExceptBranch: + if branch[0].kind == nkType: + branch[1] = ctx.convertExprBodyToAsgn(branch[1], tmp) + else: + branch[0] = ctx.convertExprBodyToAsgn(branch[0], tmp) + of nkFinally: + discard + else: + internalError("lowerStmtListExpr(nkTryStmt): " & $branch.kind) + result.add(n) + result.add(ctx.newEnvVarAccess(tmp)) + + of nkCaseStmt: + var ns = false + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], ns) + + if ns: + needsSplit = true + + let isExpr = not isEmptyType(n.typ) + + if isExpr: + let tmp = ctx.newTempVar(n.typ) + result = newNodeI(nkStmtListExpr, n.info) + result.typ = n.typ + + if n[0].kind == nkStmtListExpr: + let (st, ex) = exprToStmtList(n[0]) + result.add(st) + n[0] = ex + + for i in 1 ..< n.len: + let branch = n[i] + case branch.kind + of nkOfBranch: + branch[1] = ctx.convertExprBodyToAsgn(branch[1], tmp) + of nkElse: + branch[0] = ctx.convertExprBodyToAsgn(branch[0], tmp) + else: + internalError("lowerStmtListExpr(nkCaseStmt): " & $branch.kind) + result.add(n) + result.add(ctx.newEnvVarAccess(tmp)) + + of nkCallKinds: + var ns = false + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], ns) + + if ns: + needsSplit = true + let isExpr = not isEmptyType(n.typ) + + if isExpr: + result = newNodeI(nkStmtListExpr, n.info) + result.typ = n.typ + else: + result = newNode(nkStmtList, n.info) + + if n[0].kind == nkSym and n[0].sym.magic in {mAnd, mOr}: # `and`/`or` short cirquiting + var cond = n[1] + if cond.kind == nkStmtListExpr: + let (st, ex) = exprToStmtList(cond) + result.add(st) + cond = ex + + let tmp = ctx.newTempVar(cond.typ) + result.add(ctx.newEnvVarAsgn(tmp, cond)) + + let ifNode = newNode(nkIfStmt) + let ifBranch = newNode(nkElifBranch) + + var check = ctx.newEnvVarAccess(tmp) + if n[0].sym.magic == mOr: + check = newNotCall(check) + ifBranch.add(check) + + cond = n[2] + let ifBody = newNode(nkStmtList) + if cond.kind == nkStmtListExpr: + let (st, ex) = exprToStmtList(cond) + ifBody.add(st) + cond = ex + ifBody.add(ctx.newEnvVarAsgn(tmp, cond)) + ifBranch.add(ifBody) + ifNode.add(ifBranch) + result.add(ifNode) + result.add(ctx.newEnvVarAccess(tmp)) + else: + for i in 0 ..< n.len: + if n[i].kind == nkStmtListExpr: + let (st, ex) = exprToStmtList(n[i]) + result.add(st) + 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) + + result.add(n) + + of nkVarSection, nkLetSection: + result = newNodeI(nkStmtList, n.info) + for c in n: + let varSect = newNodeI(n.kind, n.info) + varSect.add(c) + var ns = false + c[^1] = ctx.lowerStmtListExprs(c[^1], ns) + if ns: + needsSplit = true + assert(c[^1].kind == nkStmtListExpr) + let (st, ex) = exprToStmtList(c[^1]) + result.add(st) + c[^1] = ex + result.add(varSect) + + of nkDiscardStmt, nkReturnStmt, nkRaiseStmt: + var ns = false + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], ns) + if ns: + needsSplit = true + result = newNodeI(nkStmtList, n.info) + let (st, ex) = exprToStmtList(n[0]) + result.add(st) + n[0] = ex + result.add(n) + + of nkCast: + var ns = false + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], ns) + + if ns: + needsSplit = true + result = newNodeI(nkStmtListExpr, n.info) + result.typ = n.typ + let (st, ex) = exprToStmtList(n[1]) + result.add(st) + n[1] = ex + result.add(n) + + of nkAsgn, nkFastAsgn: + var ns = false + for i in 0 ..< n.len: + n[i] = ctx.lowerStmtListExprs(n[i], ns) + + if ns: + needsSplit = true + result = newNodeI(nkStmtList, n.info) + if n[0].kind == nkStmtListExpr: + let (st, ex) = exprToStmtList(n[0]) + result.add(st) + n[0] = ex + + if n[1].kind == nkStmtListExpr: + let (st, ex) = exprToStmtList(n[1]) + result.add(st) + n[1] = ex + + result.add(n) + + of nkWhileStmt: + var ns = false + + var condNeedsSplit = false + n[0] = ctx.lowerStmtListExprs(n[0], condNeedsSplit) + var bodyNeedsSplit = false + n[1] = ctx.lowerStmtListExprs(n[1], bodyNeedsSplit) + + if condNeedsSplit or bodyNeedsSplit: + needsSplit = true + + if condNeedsSplit: + let newBody = newNode(nkStmtList) + + let (st, ex) = exprToStmtList(n[0]) + newBody.add(st) + let check = newNode(nkIfStmt) + let branch = newNode(nkElifBranch) + branch.add(newNotCall(ex)) + let brk = newNode(nkBreakStmt) + brk.add(emptyNode) + branch.add(brk) + check.add(branch) + newBody.add(check) + newBody.add(n[1]) + + n[0] = newSymNode(getSysSym("true")) + n[1] = newBody else: for i in 0 ..< n.len: - n[i] = ctx.lowerStmtListExpr(n[i]) + n[i] = ctx.lowerStmtListExprs(n[i], needsSplit) proc newEndFinallyNode(ctx: var Ctx): PNode = # Generate the following code: @@ -448,7 +793,7 @@ proc newEndFinallyNode(ctx: var Ctx): PNode = branch.add(cmp) let retStmt = newNode(nkReturnStmt) - let asgn = newNode(nkAsgn) + let asgn = newNode(nkFastAsgn) addSon(asgn, newSymNode(getClosureIterResult(ctx.fn))) addSon(asgn, ctx.newTmpResultAccess()) retStmt.add(asgn) @@ -482,7 +827,7 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode = asgn.add(newIntTypeNode(nkIntLit, 1, getSysType(tyBool))) result.add(asgn) - if n[0].kind != nkEmpty: # TODO: And not void! + if n[0].kind != nkEmpty: let asgnTmpResult = newNodeI(nkAsgn, n.info) asgnTmpResult.add(ctx.newTmpResultAccess()) asgnTmpResult.add(n[0]) @@ -508,17 +853,15 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode nkSym, nkIdent, procDefs, nkTemplateDef: discard - of nkStmtList: + of nkStmtList, nkStmtListExpr: + assert(isEmptyType(n.typ), "nkStmtListExpr not lowered") + result = addGotoOut(result, gotoOut) for i in 0 ..< n.len: if n[i].hasYieldsInExpressions: # Lower nkStmtListExpr nodes inside `n[i]` first - assert(ctx.loweredStmtListExpr.isNil) - ctx.loweredStmtListExpr = newNodeI(nkStmtList, n.info) - n[i] = ctx.lowerStmtListExpr(n[i]) - ctx.loweredStmtListExpr.add(n[i]) - n[i] = ctx.loweredStmtListExpr - ctx.loweredStmtListExpr = nil + var ns = false + n[i] = ctx.lowerStmtListExprs(n[i], ns) if n[i].hasYields: # Create a new split @@ -534,9 +877,6 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode discard ctx.transformClosureIteratorBody(s, gotoOut) break - of nkStmtListExpr: - assert(false, "nkStmtListExpr not lowered") - of nkYieldStmt: result = newNodeI(nkStmtList, n.info) result.add(n) diff --git a/tests/iter/tyieldintry.nim b/tests/iter/tyieldintry.nim index 9cb199c5b..31ec65a83 100644 --- a/tests/iter/tyieldintry.nim +++ b/tests/iter/tyieldintry.nim @@ -5,197 +5,368 @@ output: "ok" var closureIterResult = newSeq[int]() proc checkpoint(arg: int) = - closureIterResult.add(arg) + closureIterResult.add(arg) type - TestException = object of Exception - AnotherException = object of Exception + TestException = object of Exception + AnotherException = object of Exception proc testClosureIterAux(it: iterator(): int, exceptionExpected: bool, expectedResults: varargs[int]) = - closureIterResult.setLen(0) + closureIterResult.setLen(0) - var exceptionCaught = false + var exceptionCaught = false - try: - for i in it(): - closureIterResult.add(i) - except TestException: - exceptionCaught = true + try: + for i in it(): + closureIterResult.add(i) + except TestException: + exceptionCaught = true - if closureIterResult != @expectedResults or exceptionCaught != exceptionExpected: - if closureIterResult != @expectedResults: - echo "Expected: ", @expectedResults - echo "Actual: ", closureIterResult - if exceptionCaught != exceptionExpected: - echo "Expected exception: ", exceptionExpected - echo "Got exception: ", exceptionCaught - doAssert(false) + if closureIterResult != @expectedResults or exceptionCaught != exceptionExpected: + if closureIterResult != @expectedResults: + echo "Expected: ", @expectedResults + echo "Actual: ", closureIterResult + if exceptionCaught != exceptionExpected: + echo "Expected exception: ", exceptionExpected + echo "Got exception: ", exceptionCaught + doAssert(false) proc test(it: iterator(): int, expectedResults: varargs[int]) = - testClosureIterAux(it, false, expectedResults) + testClosureIterAux(it, false, expectedResults) proc testExc(it: iterator(): int, expectedResults: varargs[int]) = - testClosureIterAux(it, true, expectedResults) + testClosureIterAux(it, true, expectedResults) proc raiseException() = - raise newException(TestException, "Test exception!") + raise newException(TestException, "Test exception!") block: - iterator it(): int {.closure.} = - var i = 5 - while i != 0: - yield i - if i == 3: - yield 123 - dec i + iterator it(): int {.closure.} = + var i = 5 + while i != 0: + yield i + if i == 3: + yield 123 + dec i - test(it, 5, 4, 3, 123, 2, 1) + test(it, 5, 4, 3, 123, 2, 1) block: - iterator it(): int {.closure.} = - yield 0 - try: - checkpoint(1) - raiseException() - except TestException: - checkpoint(2) - yield 3 - checkpoint(4) - finally: - checkpoint(5) + iterator it(): int {.closure.} = + yield 0 + try: + checkpoint(1) + raiseException() + except TestException: + checkpoint(2) + yield 3 + checkpoint(4) + finally: + checkpoint(5) - checkpoint(6) + checkpoint(6) - test(it, 0, 1, 2, 3, 4, 5, 6) + test(it, 0, 1, 2, 3, 4, 5, 6) block: - iterator it(): int {.closure.} = - yield 0 - try: - yield 1 - checkpoint(2) - finally: - checkpoint(3) - yield 4 - checkpoint(5) - yield 6 + iterator it(): int {.closure.} = + yield 0 + try: + yield 1 + checkpoint(2) + finally: + checkpoint(3) + yield 4 + checkpoint(5) + yield 6 - test(it, 0, 1, 2, 3, 4, 5, 6) + test(it, 0, 1, 2, 3, 4, 5, 6) block: - iterator it(): int {.closure.} = - yield 0 - try: - yield 1 - raiseException() - yield 2 - finally: - checkpoint(3) - yield 4 - checkpoint(5) - yield 6 + iterator it(): int {.closure.} = + yield 0 + try: + yield 1 + raiseException() + yield 2 + finally: + checkpoint(3) + yield 4 + checkpoint(5) + yield 6 - testExc(it, 0, 1, 3, 4, 5, 6) + testExc(it, 0, 1, 3, 4, 5, 6) block: - iterator it(): int {.closure.} = - try: - try: - raiseException() - except AnotherException: - yield 123 - finally: - checkpoint(3) - finally: - checkpoint(4) + iterator it(): int {.closure.} = + try: + try: + raiseException() + except AnotherException: + yield 123 + finally: + checkpoint(3) + finally: + checkpoint(4) + + testExc(it, 3, 4) + +block: + iterator it(): int {.closure.} = + try: + yield 1 + raiseException() + except AnotherException: + checkpoint(123) + finally: + checkpoint(2) + checkpoint(3) - testExc(it, 3, 4) + testExc(it, 1, 2) block: - iterator it(): int {.closure.} = + iterator it(): int {.closure.} = + try: + yield 0 + try: + yield 1 try: - yield 1 - raiseException() + yield 2 + raiseException() except AnotherException: - checkpoint(123) + yield 123 finally: - checkpoint(2) - checkpoint(3) + yield 3 + except AnotherException: + yield 124 + finally: + yield 4 + checkpoint(1234) + except: + yield 5 + checkpoint(6) + finally: + checkpoint(7) + yield 8 + checkpoint(9) + + test(it, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) - testExc(it, 1, 2) +block: + iterator it(): int {.closure.} = + try: + yield 0 + return 2 + finally: + checkpoint(1) + checkpoint(123) + + test(it, 0, 1) block: - iterator it(): int {.closure.} = - try: - yield 0 - try: - yield 1 - try: - yield 2 - raiseException() - except AnotherException: - yield 123 - finally: - yield 3 - except AnotherException: - yield 124 - finally: - yield 4 - checkpoint(1234) - except: - yield 5 - checkpoint(6) - finally: - checkpoint(7) - yield 8 - checkpoint(9) + iterator it(): int {.closure.} = + try: + try: + yield 0 + raiseException() + finally: + checkpoint(1) + except TestException: + yield 2 + return + finally: + yield 3 - test(it, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) + checkpoint(123) + + test(it, 0, 1, 2, 3) block: - iterator it(): int {.closure.} = - try: - yield 0 - return 2 - finally: - checkpoint(1) - checkpoint(123) + iterator it(): int {.closure.} = + try: + try: + yield 0 + raiseException() + finally: + return # Return in finally should stop exception propagation + except AnotherException: + yield 2 + return + finally: + yield 3 + checkpoint(123) + + test(it, 0, 3) + +block: # Yield in yield + iterator it(): int {.closure.} = + template foo(): int = + yield 1 + 2 + + for i in 0 .. 2: + checkpoint(0) + yield foo() + + test(it, 0, 1, 2, 0, 1, 2, 0, 1, 2) - test(it, 0, 1) +block: + iterator it(): int {.closure.} = + let i = if true: + yield 0 + 1 + else: + 2 + yield i + + test(it, 0, 1) block: - iterator it(): int {.closure.} = - try: - try: - yield 0 - raiseException() - finally: - checkpoint(1) - except TestException: - yield 2 - return - finally: - yield 3 + iterator it(): int {.closure.} = + var foo = 123 + let i = try: + yield 0 + raiseException() + 1 + except TestException as e: + assert(e.msg == "Test exception!") + case foo + of 1: + yield 123 + 2 + of 123: + yield 5 + 6 + else: + 7 + yield i + + test(it, 0, 5, 6) - checkpoint(123) +block: + iterator it(): int {.closure.} = + proc voidFoo(i1, i2, i3: int) = + checkpoint(i1) + checkpoint(i2) + checkpoint(i3) + + proc foo(i1, i2, i3: int): int = + voidFoo(i1, i2, i3) + i3 + + proc bar(i1: int): int = + checkpoint(i1) + + template tryexcept: int = + try: + yield 1 + raiseException() + 123 + except TestException: + yield 2 + checkpoint(3) + 4 + + let e1 = true + + template ifelse1: int = + if e1: + yield 10 + 11 + else: + 12 + + template ifelse2: int = + if ifelse1() == 12: + yield 20 + 21 + else: + yield 22 + 23 + + let i = foo(bar(0), tryexcept, ifelse2) + discard foo(bar(0), tryexcept, ifelse2) + voidFoo(bar(0), tryexcept, ifelse2) + yield i + + test(it, + + # let i = foo(bar(0), tryexcept, ifelse2) + 0, # bar(0) + 1, 2, 3, # tryexcept + 10, # ifelse1 + 22, # ifelse22 + 0, 4, 23, # foo + + # discard foo(bar(0), tryexcept, ifelse2) + 0, # bar(0) + 1, 2, 3, # tryexcept + 10, # ifelse1 + 22, # ifelse22 + 0, 4, 23, # foo + + # voidFoo(bar(0), tryexcept, ifelse2) + 0, # bar(0) + 1, 2, 3, # tryexcept + 10, # ifelse1 + 22, # ifelse22 + 0, 4, 23, # foo + + 23 # i + ) - test(it, 0, 1, 2, 3) +block: + iterator it(): int {.closure.} = + checkpoint(0) + for i in 0 .. 1: + try: + yield 1 + raiseException() + except TestException as e: + doAssert(e.msg == "Test exception!") + yield 2 + except AnotherException: + yield 123 + except: + yield 1234 + finally: + yield 3 + checkpoint(4) + yield 5 + + test(it, 0, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5) block: - iterator it(): int {.closure.} = - try: - try: - yield 0 - raiseException() - finally: - return # Return in finally should stop exception propagation - except AnotherException: - yield 2 - return - finally: - yield 3 - checkpoint(123) + iterator it(): int {.closure.} = + var i = 5 + template foo(): bool = + yield i + true + + while foo(): + dec i + if i == 0: + break + + test(it, 5, 4, 3, 2, 1) + +block: # Short cirquits + iterator it(): int {.closure.} = + template trueYield: bool = + yield 1 + true + + template falseYield: bool = + yield 0 + false + + if trueYield or falseYield: + discard falseYield and trueYield + + if falseYield and trueYield: + checkpoint(123) + + test(it, 1, 0, 0) - test(it, 0, 3) echo "ok" -- cgit 1.4.1-2-gfad0