-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix #14421 items uses lent T #14447
fix #14421 items uses lent T #14447
Conversation
I think NimVersion should be its own PR and lent iterators should be serated to depend on it. |
ya I'll do that; i can use "yet another" condsym until nimVersion+nimVersionCT |
7015dde
to
2c2423c
Compare
@michael72 this PR enables faster Error: '__it__0' is of type <lent seq[uint8]> which cannot be captured as it would violate memory safety
I'd appreciate your help here. I've narrowed it down to this: import zero_functional
proc main =
let bu = @[@[1u8, 2u8], @[3u8]]
let b = bu --> map(it --> to(seq[int], true))
# let b = bu --> map(it --> it)
main() zero-functional code is rather hairy (macros calling macros calling macros) so it's hard to debug; After inserting some (proc (): auto =
var res`gensym36580008: seq[int]
result = zfInit(res`gensym36580008)
for __it__0 in __it__0:
discard
let __it__1 = __it__0
zfAddItemChk(result, -1, __it__1, "seq[uint8]", "seq[int]", true))() any help would be appreciated, including a minimal example that doesn't involve zero-functional code; |
|
Does this imply we want the borrowing rules in-place as soon as possible? |
well I'm not sure how fast is "as soon as possible" so how about I introduce a temporary opt-out (as opposed to opt-in), eg:
and I can mention |
2c2423c
to
832063d
Compare
In order to help I need some more information - could you be more specific? But could you point me to the specific build error - I don't get it. There seem to be several libraries involved. Btw - there is a simple macro |
here are detailed instructions to reproduce:
i get no warning, just an error ;-) (and so did CI until I added -d:nimWorkaround14447); I'm on OSX but shouldn't matter
thanks that helps, it shows: # t10841.nim:17
# bu.zfun:
# map(it -->> to(seq[int], true))
(proc (): auto =
{.push, hint[XDeclaredButNotUsed]: off.}
proc helperProc(): auto =
for it0 in bu:
let it1 = it0 -->> to(seq[int], true)
result = it1
{.pop.}
var res_47365200: seq[type(helperProc())]
result = zfInit(res_47365200)
for it0 in bu:
let it1 = it0 -->> to(seq[int], true)
zfAddItemChk(result, -1, it1, "seq[seq[uint8]]", "", false))()
# t10841.nim:17
# __it__0.zfun:
# to(seq[int], true)
(proc (): auto =
var res_47390009: seq[int]
result = zfInit(res_47390009)
for it0 in it0:
let it1 = it0
zfAddItemChk(result, -1, it1, "seq[uint8]", "seq[int]", true))()
# t10841.nim:17
# __it__0.zfun:
# to(seq[int], true)
(proc (): auto =
var res_47570008: seq[int]
result = zfInit(res_47570008)
for it0 in it0:
let it1 = it0
zfAddItemChk(result, -1, it1, "seq[uint8]", "seq[int]", true))() |
We can also patch zero-functional instead. I didn't know that the compiler produces an error message, I thought it produced a corruption. So the borrow rules can wait. |
for it0 in bu:
let it0 = it0 # <= add this
... rest of code (but that code is generated code, not sure where) |
@timotheecour I'm not really into quick quickfixes, but here we go. Cheers |
@timotheecour @Araq I've tried to make it as simple as possible - also using the example code that lead to the crash in the first place: proc main =
let orig = @[@[1u8, 2u8], @[3u8]]
let converted = (proc (): seq[seq[int]] =
result = @[]
for it0 in orig:
let it00 = it0
let sublist = (proc (): seq[int] =
result = @[]
for i in it0: # it00: # would work!
result.add(int(i)))()
result.add(sublist))()
echo(converted)
main() So this is: take a seq[seq[uint8]] and convert it to seq[seq[int]] - yes, this is more like a "Hey - we can do that to!" we get:
The problematic part I think - and this is regarding this PR - is the inner iterator that accesses the outer iterator I kind of "fixed" the problem on zero_functional side by adding this additional indirection - but it is somewhat a workaround, right? On the other hand it is an esoteric use case and iterators, anonymous functions and keeping all the use cases efficiently together probably very tough. So - think about it. If it leads to speed-ups it's maybe worth it, but in edge cases it is a bit weird behaviour. |
One solution is to make the compiler auto-box for |
proc main(): int =
proc bar() =
result = 1 # Error: 'result' is of type <int> which cannot be captured as it would violate memory safety
bar()
echo main() IMO this is safe so long
minimized further: proc main() =
for a in @[1,2]:
# let a = a # uncomment removes error
proc bar() =
discard a # Error: 'a' is of type <lent int> which cannot be captured as it would violate memory safety,
bar()
main()
yes but there should be exactly 0 performance penalty from before PR since it results in a copy here The ideal fix is what detecting when it's safe (no escape) and allowing it. In the meantime we could have a |
Yeah, so that the error message is no more. Let's shift the complexity to the optimizer where it belongs. |
Is it intended that this now works (unless you pass var a = @['a', 'b', 'c', 'd', 'e', 'f']
for x in a:
x = 'c'
echo a I think it should still somehow be caught at CT and not work |
no it should give CT error; looking into it more, it looks like |
* followup after Vindaar/ggplotnim#74 wrt #14447 lent iterators * ggplotnim: remove -d:nimHasWorkaround14720
Which happens because of what i think is considered a compiler bug nim-lang/Nim#14447
items
is 20%~30% slower than iteration via an index #14421items
now has no overhead over mitems or over access via indexmyseq[i]
looks like only 1 test fails:
note
future PR's
lent T
to more iterators and procslent
whenT.sizeof
is small (eg, fits in a register) since pass by value won't incur cost in this case;eg: ptr|ref|pointer|cstring