diff --git a/compiler/transf.nim b/compiler/transf.nim index 3059d8fe6b..47be5b9c61 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -22,7 +22,7 @@ import std / tables import options, ast, astalgo, trees, msgs, - idents, renderer, types, semfold, magicsys, cgmeth, + idents, renderer, types, semfold, magicsys, cgmeth, parampatterns, lowerings, liftlocals, modulegraphs, lineinfos @@ -675,7 +675,7 @@ type paDirectMapping, paFastAsgn, paFastAsgnTakeTypeFromArg paVarAsgn, paComplexOpenarray, paViaIndirection -proc putArgInto(arg: PNode, formal: PType): TPutArgInto = +proc putArgInto(arg: PNode, formal: PType; borrowedFirstArg = false): TPutArgInto = # This analyses how to treat the mapping "formal <-> arg" in an # inline context. if formal.kind == tyTypeDesc: return paDirectMapping @@ -726,6 +726,13 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto = if skipTypes(formal, abstractInst).kind in {tyVar, tyLent}: result = paVarAsgn else: result = paFastAsgn + if borrowedFirstArg and result == paDirectMapping and parampatterns.exprRoot(arg) == nil and + parampatterns.isAssignable(nil, arg) == arNone: + # Inline iterators like `items(array)` borrow from the first argument. + # If that argument is just a transient expression, materialize it so the + # lifted closure keeps the backing storage alive across yields. + result = paFastAsgnTakeTypeFromArg + proc findWrongOwners(c: PTransf, n: PNode) = if n.kind == nkVarSection: let x = n[0][0] @@ -824,13 +831,16 @@ proc transformFor(c: PTransf, n: PNode): PNode = if iter.kind != skIterator: return result # generate access statements for the parameters (unless they are constant) pushTransCon(c, newC) + let borrowedIterResult = + iter.typ != nil and iter.typ.returnType != nil and + skipTypes(iter.typ.returnType, abstractInst).kind in {tyLent, tyVar} for i in 1..= ff.n.len: return result var formal = ff.n[i].sym - let pa = putArgInto(arg, formal.typ) + let pa = putArgInto(arg, formal.typ, borrowedIterResult and i == 1) case pa of paDirectMapping: newC.mapping[formal.itemId] = arg diff --git a/lib/system/iterators.nim b/lib/system/iterators.nim index 125bee98ff..d4485ad3e9 100644 --- a/lib/system/iterators.nim +++ b/lib/system/iterators.nim @@ -3,7 +3,7 @@ when defined(nimPreviewSlimSystem): import std/assertions -when not defined(nimNoLentIterators): +when (not defined(nimNoLentIterators)) and not defined(js) and not defined(nimscript): template lent2(T): untyped = lent T else: template lent2(T): untyped = T @@ -37,7 +37,7 @@ iterator mitems*[T](a: var openArray[T]): var T {.inline.} = yield a[i] unCheckedInc(i) -iterator items*[IX, T](a: array[IX, T]): T {.inline.} = +iterator items*[IX, T](a: array[IX, T]): lent2 T {.inline.} = ## Iterates over each item of `a`. when a.len > 0: var i = low(IX) diff --git a/tests/lent/titems_array_lent.nim b/tests/lent/titems_array_lent.nim new file mode 100644 index 0000000000..f423c17963 --- /dev/null +++ b/tests/lent/titems_array_lent.nim @@ -0,0 +1,38 @@ +discard """ + targets: "c cpp js" +""" + +template sameAddress(a, b): bool = + when defined(js): + a == b + else: + a.unsafeAddr == b.unsafeAddr + +proc main() = + block: + let a = [10, 11, 12] + for ai in items(a): + doAssert sameAddress(ai, a[0]) + break + + block: + let a = [[1, 2], [1, 2], [1, 2]] + for ai in items(a): + doAssert sameAddress(ai[0], a[0][0]) + break + + block: + let s = @[(1, 2), (3, 4), (5, 6)] + doAssert (3, 4) in s + +main() + +static: + main() + +block: # issue #25849 + static: + const key = "NIM_TESTS_TOSENV_KEY" + for val in ["val", "", "\xc3\x86"]: + let s = @[(key, "val"), (key, ""), (key, "\xc3\x86")] + doAssert (key, val) in s \ No newline at end of file