From 5fa96ef270a5137510d9a21e91013cea70f4c63f Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Fri, 18 Oct 2024 10:36:41 +0200 Subject: [PATCH] Fixes #3824, fixes #19154, and hopefully #24094. Re-applies #23787. (#24316) The first commit reverts the revert of #23787. The second fixes lambdalifting in convolutedly nested closures/closureiters. This is considered to be the reason of #24094, though I can't tell for sure, as I was not able to reproduce #24094 for complicated but irrelevant reasons. Therefore I ask @jmgomez, @metagn or anyone who could reproduce it to try it again with this PR. I would suggest this PR to not be squashed if possible, as the history is already messy enough. Some theory behind the lambdalifting fix: - A closureiter that captures anything outside its body will always have `:up` in its env. This property is now used as a trigger to lift any proc that captures such a closureiter. - Instantiating a closureiter involves filling out its `:up`, which was previously done incorrectly. The fixed algorithm is to use "current" env if it is the owner of the iter declaration, or traverse through `:up`s of env param until the common ancestor is found. --------- Co-authored-by: Andreas Rumpf --- compiler/closureiters.nim | 126 +++------------- compiler/lambdalifting.nim | 76 +++++----- compiler/transf.nim | 16 +- tests/destructor/tuse_ownedref_after_move.nim | 2 +- tests/iter/tnestedclosures.nim | 139 ++++++++++++++++++ tests/iter/tyieldintry.nim | 4 +- 6 files changed, 204 insertions(+), 159 deletions(-) create mode 100644 tests/iter/tnestedclosures.nim diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 8bdd04ca78..9ee394111b 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -161,10 +161,6 @@ type # is their finally. For finally it is parent finally. Otherwise -1 idgen: IdGenerator varStates: Table[ItemId, int] # Used to detect if local variable belongs to multiple states - stateVarSym: PSym # :state variable. nil if env already introduced by lambdalifting - # remove if -d:nimOptIters is default, treating it as always nil - nimOptItersEnabled: bool # tracks if -d:nimOptIters is enabled - # should be default when issues are fixed, see #24094 const nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt, @@ -174,11 +170,8 @@ const localRequiresLifting = -2 proc newStateAccess(ctx: var Ctx): PNode = - if ctx.stateVarSym.isNil: - result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)), + result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)), getStateField(ctx.g, ctx.fn), ctx.fn.info) - else: - result = newSymNode(ctx.stateVarSym) proc newStateAssgn(ctx: var Ctx, toValue: PNode): PNode = # Creates state assignment: @@ -196,22 +189,12 @@ proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym = result.flags.incl sfNoInit assert(not typ.isNil, "Env var needs a type") - if not ctx.stateVarSym.isNil: - # We haven't gone through labmda lifting yet, so just create a local var, - # it will be lifted later - if ctx.tempVars.isNil: - ctx.tempVars = newNodeI(nkVarSection, ctx.fn.info) - addVar(ctx.tempVars, newSymNode(result)) - else: - let envParam = getEnvParam(ctx.fn) - # let obj = envParam.typ.lastSon - result = addUniqueField(envParam.typ.elementType, result, ctx.g.cache, ctx.idgen) + let envParam = getEnvParam(ctx.fn) + # let obj = envParam.typ.lastSon + result = addUniqueField(envParam.typ.elementType, result, ctx.g.cache, ctx.idgen) proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode = - if ctx.stateVarSym.isNil: - result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)), s, ctx.fn.info) - else: - result = newSymNode(s) + result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)), s, ctx.fn.info) proc newTempVarAccess(ctx: Ctx, s: PSym): PNode = result = newSymNode(s, ctx.fn.info) @@ -263,20 +246,12 @@ proc newTempVarDef(ctx: Ctx, s: PSym, initialValue: PNode): PNode = v = ctx.g.emptyNode newTree(nkVarSection, newTree(nkIdentDefs, newSymNode(s), ctx.g.emptyNode, v)) -proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode - proc newTempVar(ctx: var Ctx, typ: PType, parent: PNode, initialValue: PNode = nil): PSym = - if ctx.nimOptItersEnabled: - result = newSym(skVar, getIdent(ctx.g.cache, ":tmpSlLower" & $ctx.tempVarId), ctx.idgen, ctx.fn, ctx.fn.info) - else: - result = ctx.newEnvVar(":tmpSlLower" & $ctx.tempVarId, typ) + result = newSym(skVar, getIdent(ctx.g.cache, ":tmpSlLower" & $ctx.tempVarId), ctx.idgen, ctx.fn, ctx.fn.info) inc ctx.tempVarId result.typ = typ assert(not typ.isNil, "Temp var needs a type") - if ctx.nimOptItersEnabled: - parent.add(ctx.newTempVarDef(result, initialValue)) - elif initialValue != nil: - parent.add(ctx.newEnvVarAsgn(result, initialValue)) + parent.add(ctx.newTempVarDef(result, initialValue)) proc hasYields(n: PNode): bool = # TODO: This is very inefficient. It traverses the node, looking for nkYieldStmt. @@ -455,24 +430,13 @@ proc newTempVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode = result = newTree(nkFastAsgn, ctx.newTempVarAccess(s), v) result.info = v.info -proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode = - # unused with -d:nimOptIters - if isEmptyType(v.typ): - result = v - else: - result = newTree(nkFastAsgn, ctx.newEnvVarAccess(s), v) - result.info = v.info - proc addExprAssgn(ctx: Ctx, output, input: PNode, sym: PSym) = - var input = input if input.kind == nkStmtListExpr: let (st, res) = exprToStmtList(input) output.add(st) - input = res - if ctx.nimOptItersEnabled: - output.add(ctx.newTempVarAsgn(sym, input)) + output.add(ctx.newTempVarAsgn(sym, res)) else: - output.add(ctx.newEnvVarAsgn(sym, input)) + output.add(ctx.newTempVarAsgn(sym, input)) proc convertExprBodyToAsgn(ctx: Ctx, exprBody: PNode, res: PSym): PNode = result = newNodeI(nkStmtList, exprBody.info) @@ -601,11 +565,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = else: internalError(ctx.g.config, "lowerStmtListExpr(nkIf): " & $branch.kind) - if isExpr: - if ctx.nimOptItersEnabled: - result.add(ctx.newTempVarAccess(tmp)) - else: - result.add(ctx.newEnvVarAccess(tmp)) + if isExpr: result.add(ctx.newTempVarAccess(tmp)) of nkTryStmt, nkHiddenTryStmt: var ns = false @@ -635,10 +595,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = else: internalError(ctx.g.config, "lowerStmtListExpr(nkTryStmt): " & $branch.kind) result.add(n) - if ctx.nimOptItersEnabled: - result.add(ctx.newTempVarAccess(tmp)) - else: - result.add(ctx.newEnvVarAccess(tmp)) + result.add(ctx.newTempVarAccess(tmp)) of nkCaseStmt: var ns = false @@ -670,10 +627,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = else: internalError(ctx.g.config, "lowerStmtListExpr(nkCaseStmt): " & $branch.kind) result.add(n) - if ctx.nimOptItersEnabled: - result.add(ctx.newTempVarAccess(tmp)) - else: - result.add(ctx.newEnvVarAccess(tmp)) + result.add(ctx.newTempVarAccess(tmp)) elif n[0].kind == nkStmtListExpr: result = newNodeI(nkStmtList, n.info) let (st, ex) = exprToStmtList(n[0]) @@ -706,11 +660,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = let tmp = ctx.newTempVar(cond.typ, result, cond) # result.add(ctx.newTempVarAsgn(tmp, cond)) - var check: PNode - if ctx.nimOptItersEnabled: - check = ctx.newTempVarAccess(tmp) - else: - check = ctx.newEnvVarAccess(tmp) + var check = ctx.newTempVarAccess(tmp) if n[0].sym.magic == mOr: check = ctx.g.newNotCall(check) @@ -720,18 +670,12 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = let (st, ex) = exprToStmtList(cond) ifBody.add(st) cond = ex - if ctx.nimOptItersEnabled: - ifBody.add(ctx.newTempVarAsgn(tmp, cond)) - else: - ifBody.add(ctx.newEnvVarAsgn(tmp, cond)) + ifBody.add(ctx.newTempVarAsgn(tmp, cond)) let ifBranch = newTree(nkElifBranch, check, ifBody) let ifNode = newTree(nkIfStmt, ifBranch) result.add(ifNode) - if ctx.nimOptItersEnabled: - result.add(ctx.newTempVarAccess(tmp)) - else: - result.add(ctx.newEnvVarAccess(tmp)) + result.add(ctx.newTempVarAccess(tmp)) else: for i in 0..