Skip to content

Commit

Permalink
Merge upstream Ruby (#18)
Browse files Browse the repository at this point in the history
* Evaluate multiple assignment left hand side before right hand side

In regular assignment, Ruby evaluates the left hand side before
the right hand side.  For example:

```ruby
foo[0] = bar
```

Calls `foo`, then `bar`, then `[]=` on the result of `foo`.

Previously, multiple assignment didn't work this way.  If you did:

```ruby
abc.def, foo[0] = bar, baz
```

Ruby would previously call `bar`, then `baz`, then `abc`, then
`def=` on the result of `abc`, then `foo`, then `[]=` on the
result of `foo`.

This change makes multiple assignment similar to single assignment,
changing the evaluation order of the above multiple assignment code
to calling `abc`, then `foo`, then `bar`, then `baz`, then `def=` on
the result of `abc`, then `[]=` on the result of `foo`.

Implementing this is challenging with the stack-based virtual machine.
We need to keep track of all of the left hand side attribute setter
receivers and setter arguments, and then keep track of the stack level
while handling the assignment processing, so we can issue the
appropriate topn instructions to get the receiver.  Here's an example
of how the multiple assignment is executed, showing the stack and
instructions:

```
self                                      # putself
abc                                       # send
abc, self                                 # putself
abc, foo                                  # send
abc, foo, 0                               # putobject 0
abc, foo, 0, [bar, baz]                   # evaluate RHS
abc, foo, 0, [bar, baz], baz, bar         # expandarray
abc, foo, 0, [bar, baz], baz, bar, abc    # topn 5
abc, foo, 0, [bar, baz], baz, abc, bar    # swap
abc, foo, 0, [bar, baz], baz, def=        # send
abc, foo, 0, [bar, baz], baz              # pop
abc, foo, 0, [bar, baz], baz, foo         # topn 3
abc, foo, 0, [bar, baz], baz, foo, 0      # topn 3
abc, foo, 0, [bar, baz], baz, foo, 0, baz # topn 2
abc, foo, 0, [bar, baz], baz, []=         # send
abc, foo, 0, [bar, baz], baz              # pop
abc, foo, 0, [bar, baz]                   # pop
[bar, baz], foo, 0, [bar, baz]            # setn 3
[bar, baz], foo, 0                        # pop
[bar, baz], foo                           # pop
[bar, baz]                                # pop
```

As multiple assignment must deal with splats, post args, and any level
of nesting, it gets quite a bit more complex than this in non-trivial
cases. To handle this, struct masgn_state is added to keep
track of the overall state of the mass assignment, which stores a linked
list of struct masgn_attrasgn, one for each assigned attribute.

This adds a new optimization that replaces a topn 1/pop instruction
combination with a single swap instruction for multiple assignment
to non-aref attributes.

This new approach isn't compatible with one of the optimizations
previously used, in the case where the multiple assignment return value
was not needed, there was no lhs splat, and one of the left hand side
used an attribute setter.  This removes that optimization. Removing
the optimization allowed for removing the POP_ELEMENT and adjust_stack
functions.

This adds a benchmark to measure how much slower multiple
assignment is with the correct evaluation order.

This benchmark shows:

* 4-9% decrease for attribute sets
* 14-23% decrease for array member sets
* Basically same speed for local variable sets

Importantly, it shows no significant difference between the popped
(where return value of the multiple assignment is not needed) and
!popped (where return value of the multiple assignment is needed)
cases for attribute and array member sets.  This indicates the
previous optimization, which was dropped in the evaluation
order fix and only affected the popped case, is not important to
performance.

Fixes [Bug ruby#4443]

* * 2021-04-22 [ci skip]

* Remove reverse VM instruction

This was previously only used by the multiple assignment code, but
is no longer needed after the multiple assignment execution order
fix.

* fix raise in exception with jump

add_ensure_iseq() adds ensure block to the end of
jump such as next/redo/return. However, if the rescue
cause are in the body, this rescue catches the exception
in ensure clause.

  iter do
    next
  rescue
    R
  ensure
    raise
  end

In this case, R should not be executed, but executed without this patch.

Fixes [Bug #13930]
Fixes [Bug #16618]

A part of tests are written by @jeremyevans ruby#4291

* [ruby/time] Make Time friendly to Ractor

ruby/time@c784e4f166

* [ruby/cgi] handle invalid encoding

ruby/cgi@2b1c2e21a4

* [ruby/cgi] Add test for escapeHTML/unescapeHTML invalid encoding fix in pure ruby version

Also, remove pointless assert_nothing_raised(ArgumentError) while
here.

ruby/cgi@c05edf5608

* [ruby/cgi] gemspec: Explicitly empty executables list

The gem exposes no executables

ruby/cgi@cd7106ad97

* [ruby/benchmark] Add comment about terminating newline in captions; fix test method name.

ruby/benchmark@02ce298d3e

* [ruby/benchmark] gemspec: Explicitly have 0 executables

This gem exposes no executables.

ruby/benchmark@ff1ef7ae06

* Ignore JRuby files on io-console

* [ruby/io-console] Enable building the C extension on TruffleRuby.

ruby/io-console@c17b8cf3a9

* [ruby/io-console] Move FFI console under lib

Having the separate dir makes testing difficult and doesn't
reflect the structure the gem will eventually have. We can filter
these files out if necessary when building the CRuby gem.

ruby/io-console@881010447c

* Separate test used by test_ractor for Ractor in test_time.rb

* Merge net-imap-0.2.0

* [ruby/net-imap] Set timeout for IDLE responses

Fixes #14

ruby/net-imap@39d39ff9bb

* [ruby/net-imap] Bump version to 0.2.1

ruby/net-imap@31f96ea884

* [ruby/uri] Upstream Java proxy property checks from JRuby

These Java properties, retrieved from JRuby's "Java env" ENV_JAVA,
allow JRuby users to use the same proxy properties the rest of the
Java platform uses.

This resolves https://bugs.ruby-lang.org/issues/11194

ruby/uri@3bd2bcc95a

* [ruby/uri] Optimize URI#hostname and URI#hostname=

ruby/uri@3b7ccfd835

* [ruby/uri] Add tests for URI::RFC{2396,3986}_Parser#inspect

ruby/uri@d47dae2f8e

* [ruby/uri] Only use UnboundMethod#bind_call if it is available

This allows tests to pass on Ruby 2.4-2.6.

Fixes #19

ruby/uri@67ca99ca87

* [ruby/uri] Set required_ruby_version to 2.4 in gemspec

Tests pass on Ruby 2.4, but not on Ruby 2.3.

ruby/uri@594418079a

* [ruby/uri] remove comment about URI::escape as it is removed

ruby/uri@0f0057e1b2

* [ruby/uri] Use Regexp#match? to avoid extra allocations

`#=~` builds `MatchData`, requiring extra allocations as compared to
`#match?`, which returns a boolean w/o having to build the `MatchData`.

ruby/uri@158f58a9cc

* Update bundled_gems

* Suppress warnings for unsued variable

* * 2021-04-23 [ci skip]

* Remove unneeded comment

* test/ruby/test_assignment.rb: Avoid "assigned but unused variable"

* Fix wrong documentation

It doesn't return `nil` but raises an exception, as explained a few lines after

* * 2021-04-24 [ci skip]

* Fix setting method visibility for a refinement without an origin class

If a class has been refined but does not have an origin class,
there is a single method entry marked with VM_METHOD_TYPE_REFINED,
but it contains the original method entry.  If the original method
entry is present, we shouldn't skip the method when searching even
when skipping refined methods.

Fixes [Bug #17519]

* Remove unnecessary checks for empty kw splat

These two checks are surrounded by an if that ensures the
call site is not a kw splat call site.

* Remove part of comment that is no longer accurate

In Ruby 2.7, empty keyword splats could be added back for backwards
compatibility.  However, that stopped in Ruby 3.0.

* Add back checks for empty kw splat with tests (ruby#4405)

This reverts commit a224ce8.
Turns out the checks are needed to handle splatting an array with an
empty ruby2 keywords hash.

* [Doc] Fix a typo s/invokations/invocations/

* * 2021-04-25 [ci skip]

* [Doc] Fix a typo s/evel/eval/

* [Doc] Fix a typo s/oher/other/

* [Doc] Fix a typo s/visilibity/visibility/

* [Doc] Fix a typo s/arround/around/

* [Doc] Fix a typo s/daguten/dakuten/

* [ci skip] Fix a typo s/certificiate/certificate/

* [Doc] Fix a typo s/algorthm/algorithm/

* Fix some typos by spell checker

* * 2021-04-26 [ci skip]

* Remove test of removed reverse VM instruction

since 5512353

* spec/ruby/core/file/shared/read.rb: The behavior of FreeBSD was changed

http://rubyci.s3.amazonaws.com/freebsd12/ruby-master/log/20210426T003001Z.fail.html.gz#rubyspec

* disable shareable_constant_value for CI

To debug CI failures on FreeBSD, disable `shareable_constant_value`.

* [ruby/irb] Fix typo ture -> true [ci skip]

ruby/irb@783a0569e8

* [ruby/irb] Added assert_equal_with_term

ruby/irb@b690da96d8

* [ruby/irb] Added test_colorize

ruby/irb@10e290fc3a

* [ruby/irb] Assertions on non-tty

ruby/irb@ede12890d2

* [ruby/irb] Added `colorable` keyword option

Currently `IRB::Color.colorize` and `IRB::Color.colorize_code`
refer `$stdin.tty?` internally.
This patch adds `colorable` keyword option which overrides it.

ruby/irb@402e3f1907

* [ruby/irb] Added setup and teardown to TestIRB::TestInit

Not to be affected by existing rc files in all tests.

ruby/irb@bf434892b4

* node.c (rb_ast_new): imemo_ast is WB-unprotected

Previously imemo_ast was handled as WB-protected which caused a segfault
of the following code:

    # shareable_constant_value: literal
    M0 = {}
    M1 = {}
    ...
    M100000 = {}

My analysis is here: `shareable_constant_value: literal` creates many
Hash instances during parsing, and add them to node_buffer of imemo_ast.
However, the contents are missed because imemo_ast is incorrectly
WB-protected.

This changeset makes imemo_ast as WB-unprotected.

* Revert "disable shareable_constant_value for CI"

This reverts commit c647205.

Maybe the root issue was fixed by 7ac078e

* Document binding behavior for C call/return events for TracePoint/set_trace_func

C methods do not have bindings, so binding returns the binding of
the nearest C method.

Fixes [Bug ruby#9009]

* * 2021-04-27 [ci skip]

* Fix compiler warnings in objspace_dump.c when assertions are turned on

Example:

```
In file included from ../../../include/ruby/defines.h:72,
                 from ../../../include/ruby/ruby.h:23,
                 from ../../../gc.h:3,
                 from ../../../ext/objspace/objspace_dump.c:15:
../../../ext/objspace/objspace_dump.c: In function ‘dump_append_ld’:
../../../ext/objspace/objspace_dump.c:95:26: warning: comparison of integer expressions of different signedness: ‘long unsigned int’ and ‘int’ [-Wsign-compare]
   95 |     RUBY_ASSERT(required <= width);
      |                          ^~
```

* Fix type-o in insns.def

"redefine" -> "redefined"

* Partially revert 2c7d3b3

to make imemo_ast WB-protected again. Only the test is kept.

* Make imemo_ast WB-protected again

by firing the write barrier of imemo_ast after nd_lit is modified.
This will fix the issue of ruby#4416 more
gracefully.

* test/ruby/test_exception.rb: suppress "warning: statement not reached"

* [ruby/pathname] gemspec: Explicitly list 0 executables

This gem exposes no executables.

ruby/pathname@c401d97d58

* [ruby/gdbm] Add dependency to gdbm package on mingw

RubyInstaller2 supports metadata tags for installation of dependent
MSYS2/MINGW libraries. The openssl gem requires the mingw-openssl
package to be installed on the system, which the gem installer takes
care about, when this tag is set.

The feature is documented here:
https://github.com/oneclick/rubyinstaller2/wiki/For-gem-developers#msys2-library-dependency

Fixes oneclick/rubyinstaller2#163

ruby/gdbm@d95eed3e86

* [ruby/matrix] Use Gemfile instead of Gem::Specification#add_development_dependency.

ruby/matrix@1381fde5c1

* [ruby/matrix] v0.4.0

ruby/matrix@baea4b90d4

* [ruby/matrix] v0.4.1

ruby/matrix@f7c9981907

* [ruby/matrix] Guard for < Ruby 3.0

ruby/matrix@1ef660c627

* [ruby/net-ftp] Re-apply 827e471d438fdec1ae329afb5912b8e06d534823

ruby/net-ftp@3ca80368c4

* [ruby/net-ftp] Replace Timeout.timeout with socket timeout

Timeout.timeout is inefficient since it spins up a new thread for
each invocation, use Socket.tcp's connect_timeout option instead
when we aren't using SOCKS (we can't replace Timeout.timeout
for SOCKS yet since SOCKSSocket doesn't have a connect_timeout
option).

ruby/net-ftp@d65910132f

* [ruby/net-ftp] Close the passive connection data socket if there is an error setting up the transfer

Previously, the connection leaked in this case.  This uses
begin/ensure and checking for an error in the ensure block.

An alternative approach would be to not even perform the
connection until after the RETR (or other) command has been
sent.  However, I'm not sure all FTP servers support that.
The current behavior is:

* Send (PASV/EPSV)
* Connect to the host/port returned in 227/229 reply
* Send (RETR/other command)

Changing it to connect after the RETR could break things.
FTP servers might expect that the client has already
connected before sending the RETR.  The alternative
approach is more likely to introduce backwards compatibility
issues, compared to the begin/ensure approach taken here.

Fixes Ruby Bug 17027

ruby/net-ftp@6e8535f076

* [ruby/net-ftp] Reduce resource cosumption of Net::FTP::TIME_PARSER

Reported by Alexandr Savca as a DoS vulnerability, but Net::FTP is a
client library and the impact of the issue is low, so I have decided
to fix it as a normal issue.
Based on patch by nobu.

ruby/net-ftp@a93af636f8

* [ruby/net-ftp] Add test cases

ruby/net-ftp@865232bb2a

* [ruby/net-ftp] Replace "iff" with "if and only if"

iff means if and only if, but readers without that knowledge might
assume this to be a spelling mistake. To me, this seems like
exclusionary language that is unnecessary. Simply using "if and only if"
instead should suffice.

ruby/net-ftp@e920473618

* lldb: Add Freelist Index to dump_page output

* lldb: dump_page_rvalue - dump a heap page containing an RVALUE

rather than having to do this in a two step process:

1. heap_page obj
2. dump_page $2 (or whatever lldb variable heap_page set)

we can now just

dump_page_rvalue obj

* lldb: highlight the slot when using dump_page_rvalue

* Fix Monitor to lock per Fiber, like Mutex [Bug #17827]

* * 2021-04-28 [ci skip]

* test/ruby/test_fiber.rb: reduce the count of object creation to cause GC

... on Solaris. This is the same as 5478871.

http://rubyci.s3.amazonaws.com/solaris10-gcc/ruby-master/log/20210427T160003Z.fail.html.gz
```
[ 7667/20965] TestFiber#test_fork_from_fiber/export/home/users/chkbuild/cb-gcc/tmp/build/20210427T160003Z/ruby/test/ruby/test_fiber.rb:397:in `transfer': can't alloc machine stack to fiber (1 x 139264 bytes): Not enough space (FiberError)
	from /export/home/users/chkbuild/cb-gcc/tmp/build/20210427T160003Z/ruby/test/ruby/test_fiber.rb:397:in `block (6 levels) in test_fork_from_fiber'
	from /export/home/users/chkbuild/cb-gcc/tmp/build/20210427T160003Z/ruby/test/ruby/test_fiber.rb:396:in `times'
	from /export/home/users/chkbuild/cb-gcc/tmp/build/20210427T160003Z/ruby/test/ruby/test_fiber.rb:396:in `block (5 levels) in test_fork_from_fiber'
	from /export/home/users/chkbuild/cb-gcc/tmp/build/20210427T160003Z/ruby/test/ruby/test_fiber.rb:392:in `fork'
	from /export/home/users/chkbuild/cb-gcc/tmp/build/20210427T160003Z/ruby/test/ruby/test_fiber.rb:392:in `block (4 levels) in test_fork_from_fiber'
 = 0.88 s

...

  1) Failure:
TestFiber#test_fork_from_fiber [/export/home/users/chkbuild/cb-gcc/tmp/build/20210427T160003Z/ruby/test/ruby/test_fiber.rb:409]:
[ruby-core:41456].
<0> expected but was
<1>.
```

* test/net/ftp/test_ftp.rb: remove unused variable

* test/net/ftp/test_ftp.rb: reduce the size of a long response

"9" * 999999999 (about 1 GB) was too large for some CI servers.
This commit changes the size to 999999 (about 1 MB).

http://rubyci.s3.amazonaws.com/scw-9d6766/ruby-master/log/20210427T141707Z.fail.html.gz
http://rubyci.s3.amazonaws.com/raspbian10-aarch64/ruby-master/log/20210427T145408Z.fail.html.gz

* test/net/ftp/test_ftp.rb: Use RubyVM::JIT instead of RubyVM::MJIT

* [ruby/net-smtp] Net::SMTP.start() and #start() accepts ssl_context_params keyword argument

	Additional params are passed to OpenSSL::SSL::SSLContext#set_params.

	For example, `Net::SMTP#start(ssl_context_params: { cert_store: my_store, timeout: 123 })`
	calls `set_params({ cert_store: my_store, timeout: 123 })`.

ruby/net-smtp@4213389c21

* [ruby/net-smtp] Replace Timeout.timeout with socket timeout

Timeout.timeout is inefficient since it spins up a new thread for
each invocation, use Socket.tcp's connect_timeout option instead

ruby/net-smtp@6ae4a59f05

* [ruby/net-smtp] Removed needless files from Gem::Specification#files

ruby/net-smtp@69bba6b125

* [ruby/net-smtp] mod: bump to a new VERSION that could be checked for testings >0.2.1

ruby/net-smtp@8f2c9323e2

Co-authored-by: Jeremy Evans <[email protected]>
Co-authored-by: git <[email protected]>
Co-authored-by: Koichi Sasada <[email protected]>
Co-authored-by: Kir Shatrov <[email protected]>
Co-authored-by: pavel <[email protected]>
Co-authored-by: Olle Jonsson <[email protected]>
Co-authored-by: Keith Bennett <[email protected]>
Co-authored-by: Hiroshi SHIBATA <[email protected]>
Co-authored-by: Duncan MacGregor <[email protected]>
Co-authored-by: Charles Oliver Nutter <[email protected]>
Co-authored-by: Shugo Maeda <[email protected]>
Co-authored-by: Lukas Zapletal <[email protected]>
Co-authored-by: Felix Wong <[email protected]>
Co-authored-by: Steven Harman <[email protected]>
Co-authored-by: Kazuhiro NISHIYAMA <[email protected]>
Co-authored-by: S-H-GAMELINKS <[email protected]>
Co-authored-by: Yusuke Endoh <[email protected]>
Co-authored-by: romainsalles <[email protected]>
Co-authored-by: Alan Wu <[email protected]>
Co-authored-by: wonda-tea-coffee <[email protected]>
Co-authored-by: wonda-tea-coffee <[email protected]>
Co-authored-by: Ryuta Kamizono <[email protected]>
Co-authored-by: Nobuyoshi Nakada <[email protected]>
Co-authored-by: Peter Zhu <[email protected]>
Co-authored-by: ebrohman <[email protected]>
Co-authored-by: Lars Kanis <[email protected]>
Co-authored-by: Marc-Andre Lafortune <[email protected]>
Co-authored-by: mohamed <[email protected]>
Co-authored-by: Gannon McGibbon <[email protected]>
Co-authored-by: Matt Valentine-House <[email protected]>
Co-authored-by: Benoit Daloze <[email protected]>
Co-authored-by: Tom Freudenberg <[email protected]>
  • Loading branch information
1 parent 38c1519 commit 8476860
Show file tree
Hide file tree
Showing 92 changed files with 1,725 additions and 431 deletions.
46 changes: 46 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,58 @@ since the **3.0.0** release, except for bug fixes.
Note that each entry is kept to a minimum, see links for details.

## Language changes

* Pin operator now takes an expression. [[Feature #17411]]

```ruby
Prime.each_cons(2).lazy.find_all{_1 in [n, ^(n + 2)]}.take(3).to_a
#=> [[3, 5], [5, 7], [11, 13]]
```

* Multiple assignment evaluation order has been made consistent with
single assignment evaluation order. With single assignment, Ruby
uses a left-to-right evaluation order. With this code:

```ruby
foo[0] = bar
```

The following evaluation order is used:

1. `foo`
2. `bar`
3. `[]=` called on the result of `foo`

In Ruby before 3.1.0, multiple assignment did not follow this
evaluation order. With this code:

```ruby
foo[0], bar.baz = a, b
```

Versions of Ruby before 3.1.0 would evaluate in the following
order

1. `a`
2. `b`
3. `foo`
4. `[]=` called on the result of `foo`
5. `bar`
6. `baz=` called on the result of `bar`

Starting in Ruby 3.1.0, evaluation order is now consistent with
single assignment, with the left hand side being evaluated before
the right hand side:

1. `foo`
2. `bar`
3. `a`
4. `b`
5. `[]=` called on the result of `foo`
6. `baz=` called on the result of `bar`

[[Bug #4443]]

## Command line options

## Core classes updates
Expand Down Expand Up @@ -96,6 +141,7 @@ Excluding feature bug fixes.
## Miscellaneous changes


[Bug #4443]: https://bugs.ruby-lang.org/issues/4443
[Feature #12194]: https://bugs.ruby-lang.org/issues/12194
[Feature #14256]: https://bugs.ruby-lang.org/issues/14256
[Feature #15198]: https://bugs.ruby-lang.org/issues/15198
Expand Down
2 changes: 1 addition & 1 deletion benchmark/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Usage: benchmark-driver [options] RUBY|YAML...
--bundler Install and use gems specified in Gemfile
--filter REGEXP Filter out benchmarks with given regexp
--run-duration SECONDS Warmup estimates loop_count to run for this duration (default: 3)
-v, --verbose Verbose mode. Multiple -v options increase visilibity (max: 2)
-v, --verbose Verbose mode. Multiple -v options increase visibility (max: 2)
```

## make benchmark
Expand Down
29 changes: 29 additions & 0 deletions benchmark/masgn.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
prelude: |
a = [nil] * 3
b = Class.new{attr_writer :a, :b, :c}.new
c, d, e, f = nil, nil, nil, nil
benchmark:
array2_2: "c = (a[0], a[1] = 1, 2)"
array2_3: "c = (a[0], a[1] = 1, 2, 3)"
array3_2: "c = (a[0], a[1], a[2] = 1, 2)"
array3_3: "c = (a[0], a[1], a[2] = 1, 2, 3)"
attr2_2: "c = (b.a, b.b = 1, 2)"
attr2_3: "c = (b.a, b.b = 1, 2, 3)"
attr3_2: "c = (b.a, b.b, b.c = 1, 2)"
attr3_3: "c = (b.a, b.b, b.c = 1, 2, 3)"
lvar2_2: "c = (d, e = 1, 2)"
lvar2_3: "c = (d, e = 1, 2, 3)"
lvar3_2: "c = (d, e, f = 1, 2)"
lvar3_3: "c = (d, e, f = 1, 2, 3)"
array2_2p: "(a[0], a[1] = 1, 2; nil)"
array2_3p: "(a[0], a[1] = 1, 2, 3; nil)"
array3_2p: "(a[0], a[1], a[2] = 1, 2; nil)"
array3_3p: "(a[0], a[1], a[2] = 1, 2, 3; nil)"
attr2_2p: "(b.a, b.b = 1, 2; nil)"
attr2_3p: "(b.a, b.b = 1, 2, 3; nil)"
attr3_2p: "(b.a, b.b, b.c = 1, 2; nil)"
attr3_3p: "(b.a, b.b, b.c = 1, 2, 3; nil)"
lvar2_2p: "(d, e = 1, 2; nil)"
lvar2_3p: "(d, e = 1, 2, 3; nil)"
lvar3_2p: "(d, e, f = 1, 2; nil)"
lvar3_3p: "(d, e, f = 1, 2, 3; nil)"
2 changes: 1 addition & 1 deletion ccan/list/list.h
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ static inline void list_prepend_list_(struct list_head *to,
* @off: offset(relative to @i) at which list node data resides.
*
* This is a low-level wrapper to iterate @i over the entire list, used to
* implement all oher, more high-level, for-each constructs. It's a for loop,
* implement all other, more high-level, for-each constructs. It's a for loop,
* so you can break and continue as normal.
*
* WARNING! Being the low-level macro that it is, this wrapper doesn't know
Expand Down
Loading

0 comments on commit 8476860

Please sign in to comment.