From 3c6449dbddb28a50f8fac12c6be20f375e1f21d2 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 7 Jun 2026 19:55:56 +0200 Subject: [PATCH] fixes #25850 (#25875) --- compiler/injectdestructors.nim | 23 ++++++++++++++--- tests/arc/t25850.nim | 47 ++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 tests/arc/t25850.nim diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 0b2d085a3f..2b5ae6421e 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -803,6 +803,23 @@ proc hasCustomDestructor(c: Con, t: PType): bool = obj = skipTypes(obj.baseClass, abstractPtrs) result = result or isCustomDestructor(c, obj) +const + exprBranchKinds = {nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, + nkTryStmt, nkPragmaBlock} + +proc distributeAsgn(asgnKind: TNodeKind; dest, ri: PNode; c: var Con; s: var Scope): PNode = + ## Distributes an assignment ``dest = ri`` into the leaf expressions of + ## ``ri`` when ``ri`` is an expression-based control flow construct. This + ## avoids creating pointless intermediate temporaries (bug #25850). The + ## descent is recursive so that nestings like ``block: ...; if c: a else: b`` + ## assign directly to ``dest`` instead of going through a temp per branch. + if ri.kind in exprBranchKinds: + template process(child, s): untyped = + distributeAsgn(asgnKind, dest, child, c, s) + handleNestedTempl(ri, process, willProduceStmt = true) + else: + result = newTree(asgnKind, dest, p(ri, c, s, consumed)) + proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}; inReturn = false): PNode = if n.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt, nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkParForStmt, nkTryStmt, nkPragmaBlock}: @@ -1004,13 +1021,11 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s, flags) elif isDiscriminantField(n[0]): result = c.genDiscriminantAsgn(s, n) - elif n[1].kind in {nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt, nkPragmaBlock}: + elif n[1].kind in exprBranchKinds: # Distribute the assignment into each branch to avoid # creating pointless temporaries for expression-based control flow. let dest = p(n[0], c, s, mode) - template process(child, s): untyped = - newTree(n.kind, dest, p(child, c, s, consumed)) - handleNestedTempl(n[1], process, willProduceStmt = true) + result = distributeAsgn(n.kind, dest, n[1], c, s) else: result = copyNode(n) result.add p(n[0], c, s, mode) diff --git a/tests/arc/t25850.nim b/tests/arc/t25850.nim new file mode 100644 index 0000000000..abdb772d37 --- /dev/null +++ b/tests/arc/t25850.nim @@ -0,0 +1,47 @@ +discard """ + cmd: '''nim c --mm:orc --expandArc:uIf --expandArc:uCase $file''' + nimout: ''' +--expandArc: uIf + +block :tmp: + let s = w() + if true: + r[] = s + else: + r[] = s +-- end of expandArc ------------------------ +--expandArc: uCase + +block :tmp: + let s = w() + case n + of 0: + r[] = s + else: + r[] = w() +-- end of expandArc ------------------------ +''' +""" + +# bug #25850 +# Assigning an expression-based control flow construct (an `if`/`case` nested in +# a `block`) must distribute the assignment directly into the leaf branches +# instead of creating redundant intermediate temporaries per branch. + +proc w(): array[1000, byte] {.noinline.} = discard + +proc uIf(r: ptr array[1000, byte]) = + r[] = (block: + let s = w() + if true: s else: s) + +proc uCase(r: ptr array[1000, byte], n: int) = + r[] = (block: + let s = w() + case n + of 0: s + else: w()) + +var d: array[1000, byte] +uIf(addr d) +uCase(addr d, 0)