Skip to content
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

fixes #8577, #8580, other bug fixes #8584

Merged
merged 4 commits into from
Aug 14, 2018
Merged

Conversation

timotheecour
Copy link
Member

@timotheecour timotheecour commented Aug 9, 2018

/c @Araq @LemonBoy more fixes coming after this PR, eg: #8586 (+ 2 others) which depend on this to be merged first ; so can we merge this sooner rather than later?

Copy link
Contributor

@LemonBoy LemonBoy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more failing test case:

[].mapIt(it)

when compiles(s.len):
evalOnce(t, s)
# using t_D20180808T211500 to avoid https://github.com/nim-lang/Nim/issues/8580
evalOnce(t_D20180808T211500, s, type(s) isnot openArray)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is nasty. Using a block: is a somewhat cleaner workaround.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of checking whether s is an openArray I'd just add another compiles((let _ = s)) to the when above.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This usage of compiles seems to be a hack... :-/

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, but I can't think of a better way to shunt the bare openArray literals into the other code path

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is nasty. Using a block: is a somewhat cleaner workaround.

used a block; much cleaner, thanks!

compiles((let _ = s))

had thought of that but will likely lead to other bugs and hide other issues

Yep, but I can't think of a better way to shunt the bare openArray literals into the other code path

not sure what you're suggesting besides compiles ; what could go wrong with type(s) isnot openArray ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as discussed here https://gitter.im/nim-lang/Nim?at=5b6c0cf3637ee66082c775e8, I changed to compiles((let _ = s)) (see comments in code)

@timotheecour
Copy link
Member Author

One more failing test case: [].mapIt(it)

illegal (just as let a =@[] is), however added test case for (typed) empty seqs.

PTAL (all comments addressed)

@timotheecour
Copy link
Member Author

PTAL all comments addressed (after fix to compiles((let _ = s))

doAssert foo([1,2,3]) == @[2,3,4]

block: # mapIt with direct openArray
# see https://github.com/_render_node/MDExOlB1bGxSZXF1ZXN0MjA3MTQ3MTIw/pull_requests/timeline#issuecomment-411614711
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the github.com/_render_node/.. link?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah... this URL doesn't load.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed it

Copy link
Contributor

@dom96 dom96 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall the PR is fine. If I wasn't so picky and unfamiliar with this code I would have merged it :)

# literal would be substituted twice
# BUG: `evalOnce(t, s, false)` would lead to C compile errors
# (`error: use of undeclared identifier`) instead of Nim compile errors
evalOnce(t, s, compiles((let _ = s)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

t, s

I know that you didn't introduce these names but I really despise single character variable names. In reviewing this PR I had to go back and forth between various templates to work out what t is meant to be here.

Perhaps they could be changed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: this evalOnce macro feels like a total hack to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The compiles check should be moved above in the enclosing if, you don't need the third parameter.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

almost procs in this module use single char arg names so I don't wanna special case mapIt, feel free to change that in a subsequent PR.
change s to s2 to make it obvious it relates to s; also improved var names of evalOnce and added a bunch of documentation.

Side note: this evalOnce macro feels like a total hack to me.

feel free to improve that in a subsequent PR. It's there for a reason (avoid double evaluation bugs, handle openArray and other edge cases) and neither @LemonBoy nor I could come up with something better

doAssert foo([1,2,3]) == @[2,3,4]

block: # mapIt with direct openArray
# see https://github.com/_render_node/MDExOlB1bGxSZXF1ZXN0MjA3MTQ3MTIw/pull_requests/timeline#issuecomment-411614711
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah... this URL doesn't load.


block: # mapIt with direct openArray
# see https://github.com/_render_node/MDExOlB1bGxSZXF1ZXN0MjA3MTQ3MTIw/pull_requests/timeline#issuecomment-411614711
doAssert openArray[int]([1,2]).mapIt(it) == @[1,2]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A better test would be to create a proc that takes an openarray. Pass it a list and call mapIt on the argument.

Frankly, I'm surprised this works. openArray is a pseudo-type AFAIK.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i already have a test for that, see "mapIt with openArray"

doAssert openArray[int]([1,2]).mapIt(it) == @[1,2]

block: # mapIt test, see https://github.com/nim-lang/Nim/pull/8584#pullrequestreview-144723468
# NOTE: `[].mapIt(it)` is illegal, just as `let a = @[]` is
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about

mapIt([], it)

mapIt(@[], it)

?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a test doAssert: not compiles(mapIt(@[], it)) ; it should indeed not compile as explained in comments (lacks type information)

@timotheecour
Copy link
Member Author

PTAL. all comments addressed.


# BUG: `evalOnceAs(s2, s, false)` would lead to C compile errors
# (`error: use of undeclared identifier`) instead of Nim compile errors
evalOnceAs(s2, s, compiles((let _ = s)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This compiles(...) block should go with compiles(s.len).
If you don't do so and s is a openArray[int](...) the template expansion below will replicate s two times like this:

...
var result = newSeq[outType](openArray[int](...).len)
for it {.inject.} in openArray[int](...):
  ...

This edge case is better handled (IMO) by the other code path even though we don't make use of the .len, after all this is an edge case as you don't go and cast openarrays.

Copy link
Member Author

@timotheecour timotheecour Aug 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know about this limitation (it's documented in the PR, see below) but I disagree with your proposed change:

  • with your suggestion, it'll affect valid (non edge case) code paths, resulting in un-optimized code for these.

please try the PR with your suggestion and see what happens by inserting these echo statements:

  when compiles(s.len) and compiles((let _ = s)):
    block:
      evalOnceAs(s2, s, true)
      echo "optimized"
      var i = 0
      var result = newSeq[outType](s2.len)
      for it {.inject.} in s2:
        result[i] = op
        i += 1
      result
  else:
    echo "fallback"
    var result: seq[outType] = @[]
    for it {.inject.} in s:
      result.add(op)
    result

will print:

optimized
fallback
fallback
fallback
optimized
optimized
optimized
optimized
fallback

whereas with current PR it prints:

optimized
optimized
optimized
optimized
optimized
optimized
optimized
optimized
fallback

ie it does the correct thing (optimized code + no duplicate evaluation) in all cases except the in the very edge case of an openArray constructor passed to a proc (template would be ok).

copying verbatim from PR:

  block: # mapIt with direct openArray
    proc foo1(x: openArray[int]): seq[int] = x.mapIt(it * 10)
    counter = 0
    doAssert foo1(openArray[int]([identity(1),identity(2)])) == @[10,20]
    doAssert counter == 2

    template foo2(x: openArray[int]): seq[int] = x.mapIt(it * 10)
    counter = 0
    doAssert foo2(openArray[int]([identity(1),identity(2)])) == @[10,20]
    # TODO: this fails; not sure how to fix this case
    # doAssert counter == 2

Note also that this edge case behavior is pre-existing to this PR, so I see now reason we can't merge this now; we can always do a subsequent PR later if we find a way that doesn't affect non-edge cases adversly while handling this edge case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, that's fine by me then.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool. PR still marked as @LemonBoy requested changes though...

@timotheecour
Copy link
Member Author

PTAL: can we please get this merged ASAP? it fixes pre-existing bugs

@Araq
Copy link
Member

Araq commented Aug 14, 2018

it fixes pre-existing bugs

And we're sure it doesn't introduce new bugs, right?

Oh well, will clean this up later.

@Araq Araq merged commit ed9fd2b into nim-lang:devel Aug 14, 2018
@timotheecour
Copy link
Member Author

thank you.

And we're sure it doesn't introduce new bugs, right?

obviously no such guarantees, but it came with increased test coverage

@timotheecour timotheecour deleted the exp_mapIt_simple branch August 14, 2018 09:12
timotheecour added a commit to timotheecour/Nim that referenced this pull request Aug 16, 2018
Araq pushed a commit that referenced this pull request Aug 16, 2018
PMunch pushed a commit to PMunch/Nim that referenced this pull request Aug 17, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants