Project

General

Profile

Bug #12860

Splatting an argument does not obey left-to-right execution order

Added by headius (Charles Nutter) over 2 years ago. Updated about 2 years ago.

Status:
Closed
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:77701]

Description

Ruby evaluates arguments left to right, but it does not appear to handle construction of the eventual argument list from left to right.

Take this example:

def foo(*args)
  p args
end

ary = [1,2]

foo(*ary, ary.shift)

With left-to-right execution, the ary value should be splatted (1, 2), and THEN shifted (1) producing args == [1, 2, 1].

However, on MRI, the shift occurs before the splat, so args == [2, 1].

This is counter-intuitive. At the moment in time the splat is encountered, ary is still [1, 2]. So the first two arguments should be (1, 2). THEN the shift happens, producing a third argument of (1).

This affects JRuby running Rails because they have a small piece of code that depends on this unusual behavior: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/callbacks.rb#L411-L425

This code appears to have been introduced into Rails recently, and I will file a separate issue to change it to be more explicit about ordering.

Associated revisions

Revision a078bbcc
Added by usa (Usaku NAKAMURA) about 2 years ago

merge revision(s) 56469: [Backport #12860]

* compile.c (setup_args): duplicate splatting array if more
  arguments present to obey left-to-right execution order.
  [ruby-core:77701] [Bug# 12860]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_2@57210 b2dd03c8-39d4-4d8f-98ff-823fe69b080e

Revision 57210
Added by usa (Usaku NAKAMURA) about 2 years ago

merge revision(s) 56469: [Backport #12860]

* compile.c (setup_args): duplicate splatting array if more
  arguments present to obey left-to-right execution order.
  [ruby-core:77701] [Bug# 12860]

Revision 3032b62d
Added by nagachika (Tomoyuki Chikanaga) about 2 years ago

merge revision(s) 56469: [Backport #12860]

* compile.c (setup_args): duplicate splatting array if more
  arguments present to obey left-to-right execution order.
  [ruby-core:77701] [Bug# 12860]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_3@57342 b2dd03c8-39d4-4d8f-98ff-823fe69b080e

Revision 57342
Added by nagachika (Tomoyuki Chikanaga) about 2 years ago

merge revision(s) 56469: [Backport #12860]

* compile.c (setup_args): duplicate splatting array if more
  arguments present to obey left-to-right execution order.
  [ruby-core:77701] [Bug# 12860]

History

Updated by headius (Charles Nutter) over 2 years ago

Rails PR (which unpacks via multi-assign to avoid this bug) filed here: https://github.com/rails/rails/pull/26854

Updated by headius (Charles Nutter) over 2 years ago

This behavior appears to go back as far as Ruby 1.9.3. Ruby 1.8.7 and earlier could not have regular arguments after a splat.

Updated by headius (Charles Nutter) over 2 years ago

It is worth mentioning that if the splatted value is not an array, we can see that to_a does get called before shift. That makes this even more unintuitive, since the calls happen in proper order but the assembly of the args list happens in the wrong order.

a = Object.new

a.instance_variable_set :@ary, [1, 2]

def a.to_a
  p :to_a
  @ary
end

def a.shift
  p :shift
  @ary.shift
end

def f(*x)
  p x
end

f(*a, a.shift)

__END__

Output on MRI:

:to_a
:shift
[2, 1]

Output on JRuby:

:to_a
:shift
[1, 2, 1]

All implementations I tested output to_a before shift. However, as with the plain array, MRI delays the application of the splat until after the shift call.

Updated by nobu (Nobuyoshi Nakada) about 2 years ago

  • Backport changed from 2.1: UNKNOWN, 2.2: UNKNOWN, 2.3: UNKNOWN to 2.1: REQUIRED, 2.2: REQUIRED, 2.3: REQUIRED
  • Status changed from Open to Closed

Updated by usa (Usaku NAKAMURA) about 2 years ago

  • Backport changed from 2.1: REQUIRED, 2.2: REQUIRED, 2.3: REQUIRED to 2.1: REQUIRED, 2.2: DONE, 2.3: REQUIRED

ruby_2_2 r57210 merged revision(s) 56469.

Updated by nagachika (Tomoyuki Chikanaga) about 2 years ago

  • Backport changed from 2.1: REQUIRED, 2.2: DONE, 2.3: REQUIRED to 2.1: REQUIRED, 2.2: DONE, 2.3: DONE

ruby_2_3 r57342 merged revision(s) 56469.

Also available in: Atom PDF