Move analysis missing in closure iterator
Nim Version
2.2.4, 86bbc73b3ab281ed6f57da88d6cf05e899714c13
Description
In ORC, the following snippet does not compile, though it should.
In refc, the snippet does compile but copies NoCopy even though it should move it. In fact, 5 (!) copies of "test" happen on its journey to the echo.
type NoCopy = object
v: string
proc `=copy`(a: var NoCopy, b: NoCopy) {.error.}
proc sinkMe(v: sink NoCopy) =
echo v.v
proc iter(v: sink NoCopy): iterator(): int =
iterator(): int =
yield 1
sinkMe(v)
proc main() =
let vvv = iter(NoCopy(v: "test"))
for i in vvv():
echo i
main()
Current Output
testit.nim(13, 12) Error: '=dup' is not available for type <sink NoCopy>, which is inferred from unavailable '=copy'; requires a copy because it's not the last read of ':envP.`:up`.v0'; routine: :anonymous
Expected Output
Known Workarounds
No response
Additional Information
No response
expansions:
while true:
block :stateLoop:
case :envP.`:state`
of 0:
:envP.`:state` = 1
return 1
of 1:
sinkMe(:envP.`:up`.v0)
:envP.`:state` = -1
break :stateLoop
else:
return
CFG:
L0:
0 use :envP.`:state` #21
1 fork L5 2 def :envP.`:state` #21
3 goto L16 4 goto L11L5:
5 fork L10 6 use :envP.`:up`.v0 #23
7 def :envP.`:state` #21
8 goto L12 9 goto L11L10:
10 goto L17L11:
11 goto L13L12:
12 goto L13L13:
13 loop L0 14 goto L15L15:
15 goto L18L16:
16 goto L18L17:
17 goto L18L18: End
move analysis doesn't seem to think of this as a last read
Something to be careful about is that in the case of an iterator, the iterator steps through its states in a controlled manner and reaches an end (usually - similar to the same code in a non-iterator, the point being that it can only go through the body "once" with any one particular closure environment) - a closure procedure on the other hand reuses its closure environment over and over so the analysis is different - it means, among other things, that the code is not correct for:
type NoCopy = object
v: string
proc `=copy`(a: var NoCopy, b: NoCopy) {.error.}
proc sinkMe(v: sink NoCopy) =
echo v.v
proc p(v: sink NoCopy): proc(): int =
proc() int =
sinkMe(v) # should error here
1
proc main() =
let vvv = p()(NoCopy(v: "test"))
echo vvv()
echo vvv() # Can't move v because called might do this
main()