aboutsummaryrefslogtreecommitdiffstats
path: root/test/ruby/test_keyword.rb
Commit message (Collapse)AuthorAgeFilesLines
* clear `kw_flag` if given hash is nilKoichi Sasada2024-06-131-0/+18
| | | | | https://bugs.ruby-lang.org/issues/20570 is caused I missed to clear the `kw_flag` even if `keyword_hash` is nil.
* Do not apply anon_rest optimization when passed array uses keyword-flagged hashJeremy Evans2024-03-221-0/+13
| | | | | | | | The optimization sets args->rest_dupped to avoid allocating an array, but this is not safe if the splat array ends in a keyword flagged hash. Unset args->rest_dupped in this case. Fixes [Bug #20388]
* Correctly set anon_kwrest flag for def f(b: 1, **)Jeremy Evans2024-03-011-0/+7
| | | | | | | | | | | | | | | | | | | | | In cases where a method accepts both keywords and an anonymous keyword splat, the method was not marked as taking an anonymous keyword splat. Fix that in the compiler. Doing that broke handling of nil keyword splats in yjit, so update yjit to handle that. Add a test to check that calling a method that accepts both a keyword argument and an anonymous keyword splat does not modify a passed keyword splat hash. Move the anon_kwrest check from setup_parameters_complex to ignore_keyword_hash_p, and only use it if the keyword hash is already a hash. This should speed things up slightly as it avoids a check previously used for all callers of setup_parameters_complex. Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
* Allow `foo(**nil, &block_arg)`Alan Wu2024-02-121-0/+1
| | | | | | | | Previously, `**nil` by itself worked, but if you add a block argument, it raised a conversion error. The presence of the block argument shouldn't change how keyword splat works. See: <https://bugs.ruby-lang.org/issues/20064>
* Fix crash when passing large keyword splat to method accepting keywords and ↵Jeremy Evans2024-02-111-0/+14
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | keyword splat The following code previously caused a crash: ```ruby h = {} 1000000.times{|i| h[i.to_s.to_sym] = i} def f(kw: 1, **kws) end f(**h) ``` Inside a thread or fiber, the size of the keyword splat could be much smaller and still cause a crash. I found this issue while optimizing method calling by reducing implicit allocations. Given the following code: ```ruby def f(kw: , **kws) end kw = {kw: 1} f(**kw) ``` The `f(**kw)` call previously allocated two hashes callee side instead of a single hash. This is because `setup_parameters_complex` would extract the keywords from the keyword splat hash to the C stack, to attempt to mirror the case when literal keywords are passed without a keyword splat. Then, `make_rest_kw_hash` would build a new hash based on the extracted keywords that weren't used for literal keywords. Switch the implementation so that if a keyword splat is passed, literal keywords are deleted from the keyword splat hash (or a copy of the hash if the hash is not mutable). In addition to avoiding the crash, this new approach is much more efficient in all cases. With the included benchmark: ``` 1 miniruby: 5247879.9 i/s miniruby-before: 2474050.2 i/s - 2.12x slower 1_mutable miniruby: 1797036.5 i/s miniruby-before: 1239543.3 i/s - 1.45x slower 10 miniruby: 1094750.1 i/s miniruby-before: 365529.6 i/s - 2.99x slower 10_mutable miniruby: 407781.7 i/s miniruby-before: 225364.0 i/s - 1.81x slower 100 miniruby: 100992.3 i/s miniruby-before: 32703.6 i/s - 3.09x slower 100_mutable miniruby: 40092.3 i/s miniruby-before: 21266.9 i/s - 1.89x slower 1000 miniruby: 21694.2 i/s miniruby-before: 4949.8 i/s - 4.38x slower 1000_mutable miniruby: 5819.5 i/s miniruby-before: 2995.0 i/s - 1.94x slower ```
* Do not modify provided argument splat when using ruby2_keywords with ↵Jeremy Evans2024-01-311-0/+18
| | | | | | | | | anonymous splat Previously, this would push the provided keywords onto the argument splat. Add ruby2_keywords to the list of other checks for whether it is safe for treating a given splat as mutable when the called method accepts an anonymous splat.
* Support keyword splatting nilJeremy Evans2024-01-141-0/+38
| | | | | | | | nil is treated similarly to the empty hash in this case, passing no keywords and not calling any conversion methods. Fixes [Bug #20064] Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
* Fix keyword splat passing as regular argumentJeremy Evans2023-12-071-6/+20
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Since Ruby 3.0, Ruby has passed a keyword splat as a regular argument in the case of a call to a Ruby method where the method does not accept keyword arguments, if the method call does not contain an argument splat: ```ruby def self.f(obj) obj end def self.fs(*obj) obj[0] end h = {a: 1} f(**h).equal?(h) # Before: true; After: false fs(**h).equal?(h) # Before: true; After: false a = [] f(*a, **h).equal?(h) # Before and After: false fs(*a, **h).equal?(h) # Before and After: false ``` The fact that the behavior differs when passing an empty argument splat makes it obvious that something is not working the way it is intended. Ruby 2 always copied the keyword splat hash, and that is the expected behavior in Ruby 3. This bug is because of a missed check in setup_parameters_complex. If the keyword splat passed is not mutable, then it points to an existing object and not a new object, and therefore it must be copied. Now, there are 3 specs for the broken behavior of directly using the keyword splatted hash. Fix two specs and add a new version guard. Do not keep the specs for the broken behavior for earlier Ruby versions, in case this fix is backported. For the ruby2_keywords spec, just remove the related line, since that line is unrelated to what the spec is testing. Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
* `Hash#dup` for kwsplat argumentsKoichi Sasada2023-03-151-0/+10
| | | | | | | On `f(*a, **kw)` method calls, a rest keyword parameter is identically same Hash object is passed and it should make `#dup`ed Hahs. fix https://bugs.ruby-lang.org/issues/19526
* Rename method name in keyword test from y to yoJeremy Evans2022-09-271-107/+107
| | | | | Kernel#y is defined by psych/yaml, which causes occasional nondeterministic problems with this test when doing parallel testing.
* test/ruby/test_keyword.rb: Prevent warning: assigned but unused variableYusuke Endoh2022-04-111-1/+0
|
* Unflag a splatted flagged hash if the method doesn't use ruby2_keywordsJeremy Evans2022-04-051-0/+48
| | | | | | | | | | | | | | | | | | | | | | | | For a method such as: def foo(*callee_args) end If this method is called with a flagged hash (created by a method flagged with ruby2_keywords), this previously passed the hash through without modification. With this change, it acts as if the last hash was passed as keywords, so a call to: foo(*caller_args) where the last element of caller_args is a flagged hash, will be treated as: foo(*caller_args[0...-1], **caller_args[-1]) As a result, inside foo, callee_args[-1] is an unflagged duplicate of caller_args[-1] (all other elements of callee_args match caller_args). Fixes [Bug #18625]
* Do not autosplat array in block call just because keywords acceptedJeremy Evans2022-03-301-1/+1
| | | | | | | | | | | If the block only accepts a single positional argument plus keywords, then do not autosplat. Still autosplat if the block accepts more than one positional argument in addition to keywords. Autosplatting a single positional argument plus keywords made sense in Ruby 2, since a final positional hash could be used as keywords, but it does not make sense in Ruby 3. Fixes [Bug #18633]
* Adds mixed hash value and value omission testsBrandon Weaver2021-09-131-0/+1
| | | | | Introduces specification tests for mixed values and value omissions for Hashes and keyword arguments, such as `{ a:, b:, c: 3 }`.
* Add documentation and tests for keyword argument value omissionShugo Maeda2021-09-111-0/+16
| | | | [Feature #14579]
* Add back checks for empty kw splat with tests (#4405)Alan Wu2021-04-231-0/+20
| | | | | This reverts commit a224ce8150f2bc687cf79eb415c931d87a4cd247. Turns out the checks are needed to handle splatting an array with an empty ruby2 keywords hash.
* Fixed premature returnNobuyoshi Nakada2021-01-191-0/+9
| | | | | After setting ruby2_keywords for bmethod, the rest of arguments had been ignored. [Bug #17558]
* Dup kwrest hash when merging other keyword arguments [Bug #17481]Nobuyoshi Nakada2020-12-281-1/+24
|
* Reduce allocations for keyword argument hashesJeremy Evans2020-03-171-0/+212
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Previously, passing a keyword splat to a method always allocated a hash on the caller side, and accepting arbitrary keywords in a method allocated a separate hash on the callee side. Passing explicit keywords to a method that accepted a keyword splat did not allocate a hash on the caller side, but resulted in two hashes allocated on the callee side. This commit makes passing a single keyword splat to a method not allocate a hash on the caller side. Passing multiple keyword splats or a mix of explicit keywords and a keyword splat still generates a hash on the caller side. On the callee side, if arbitrary keywords are not accepted, it does not allocate a hash. If arbitrary keywords are accepted, it will allocate a hash, but this commit uses a callinfo flag to indicate whether the caller already allocated a hash, and if so, the callee can use the passed hash without duplicating it. So this commit should make it so that a maximum of a single hash is allocated during method calls. To set the callinfo flag appropriately, method call argument compilation checks if only a single keyword splat is given. If only one keyword splat is given, the VM_CALL_KW_SPLAT_MUT callinfo flag is not set, since in that case the keyword splat is passed directly and not mutable. If more than one splat is used, a new hash needs to be generated on the caller side, and in that case the callinfo flag is set, indicating the keyword splat is mutable by the callee. In compile_hash, used for both hash and keyword argument compilation, if compiling keyword arguments and only a single keyword splat is used, pass the argument directly. On the caller side, in vm_args.c, the callinfo flag needs to be recognized and handled. Because the keyword splat argument may not be a hash, it needs to be converted to a hash first if not. Then, unless the callinfo flag is set, the hash needs to be duplicated. The temporary copy of the callinfo flag, kw_flag, is updated if a hash was duplicated, to prevent the need to duplicate it again. If we are converting to a hash or duplicating a hash, we need to update the argument array, which can including duplicating the positional splat array if one was passed. CALLER_SETUP_ARG and a couple other places needs to be modified to handle similar issues for other types of calls. This includes fairly comprehensive tests for different ways keywords are handled internally, checking that you get equal results but that keyword splats on the caller side result in distinct objects for keyword rest parameters. Included are benchmarks for keyword argument calls. Brief results when compiled without optimization: def kw(a: 1) a end def kws(**kw) kw end h = {a: 1} kw(a: 1) # about same kw(**h) # 2.37x faster kws(a: 1) # 1.30x faster kws(**h) # 2.19x faster kw(a: 1, **h) # 1.03x slower kw(**h, **h) # about same kws(a: 1, **h) # 1.16x faster kws(**h, **h) # 1.14x faster
* Do not autosplat when calling procs that accept rest and keywordsJeremy Evans2020-03-081-7/+12
| | | | | | | | | When providing a single array to a block that takes a splat, pass the array as one argument of the splat instead of as the splat itself, even if the block also accepts keyword arguments. Previously, this behavior was only used for blocks that did not accept keywords. Implements [Feature#16166]
* Check type of empty keyword [Bug #16603]Seiei Miyagi2020-02-031-0/+6
| | | | Co-authored-by: Yusuke Endoh <mame@ruby-lang.org>
* Remove empty keyword splats when calling even when using ruby2_keywordsJeremy Evans2020-01-231-4/+4
| | | | | | | | | Keeping empty keyword splats for ruby2_keywords methods was necessary in 2.7 to prevent the final positional hash being treated as keywords. Now that keyword argument separation has been committed, the final positional hash is never treated as keywords, so there is no need to keep empty keyword splats when using ruby2_keywords.
* Suppress some warningsYusuke Endoh2020-01-071-1/+0
| | | | | | | | | ``` .../ruby/test/ruby/test_keyword.rb:3509: warning: assigned but unused variable - bug8993 .../ruby/test/ruby/test_object.rb:83: warning: assigned but unused variable - f .../ruby/test/ruby/test_object.rb:95: warning: method redefined; discarding old initialize_clone .../ruby/test/ruby/test_object.rb:84: warning: previous definition of initialize_clone was here ```
* Update tests for full keyword argument separationJeremy Evans2020-01-021-1450/+470
|
* Check Module#ruby2_keywords arityNobuyoshi Nakada2020-01-021-0/+6
| | | | | It is considered a mistake, because calling this method with no arguments has no effect.
* Reword keyword arguments warning messages to convey these are deprecation ↵Marc-Andre Lafortune2019-12-231-435/+435
| | | | warnings
* compile.c: avoid newarraykwsplat for argumentsv2_7_0_rc2Yusuke Endoh2019-12-221-0/+6
| | | | | | | | | | | | | | `foo(*rest, post, **empty_kw)` is compiled like `foo(*rest + [post, **empty_kw])`, and `**empty_kw` is removed by "newarraykwsplat" instruction. However, the method call still has a flag of KW_SPLAT, so "post" is considered as a keyword hash, which caused a segfault. Note that the flag cannot be removed if "empty_kw" is not always empty. This change fixes the issue by compiling arguments with "newarray" instead of "newarraykwsplat". [Bug #16442]
* vm_args.c: rephrase the warning message of keyword argument separationYusuke Endoh2019-12-201-308/+308
| | | | | | | | | | (old) test.rb:4: warning: The last argument is used as the keyword parameter test.rb:1: warning: for `foo' defined here; maybe ** should be added to the call? (new) test.rb:4: warning: The last argument is used as keyword parameters; maybe ** should be added to the call test.rb:1: warning: The called method `foo' is defined here
* test/ruby/test_keywords.rb: suppress a warningYusuke Endoh2019-12-101-1/+1
| | | | | | | https://rubyci.org/logs/rubyci.s3.amazonaws.com/ubuntu1604/ruby-master/log/20191210T003005Z.log.html.gz ``` .../test/ruby/test_keyword.rb:2711: warning: `*' interpreted as argument prefix ```
* Add Proc#ruby2_keywordsJeremy Evans2019-12-091-0/+39
| | | | | | | | | | | | This allows passing keywords through a normal argument splat in a Proc. While needing ruby2_keywords support for methods is more common, there is code that delegates keywords through normal argument splats in procs, including code in Rails. For that reason, it makes sense to expose this for procs as well. Internally, ruby2_keywords is not tied to methods, but iseqs, so this just allows for setting the ruby2_keywords for the iseq related to the proc.
* Reduce duplicated warnings for the change of Ruby 3 keyword argumentsYusuke Endoh2019-11-291-28/+138
| | | | | | | | | | | | | | | By this change, the following code prints only one warning. ``` def foo(**opt); end 100.times { foo({kw:1}) } ``` A global variable `st_table *caller_to_callees` is a map from caller to a set of callee methods. It remembers that a warning is already printed for each pair of caller and callee. [Feature #16289]
* vm_method.c: add top-level ruby2_keywordsYusuke Endoh2019-11-291-0/+12
| | | | | | This is a top-level version of Module#ruby2_keywords. It can be used for functions (top-level methods) that delegates arguments. [Feature #16364]
* Don't modify rest array when using ruby2_keywordsJeremy Evans2019-11-271-0/+9
| | | | | | Previously, the rest array was modified, but it turns out that is not necessary. Not modifying the rest array fixes cases when the rest array is used more than once.
* fix bug in keyword + protected combination卜部昌平2019-10-281-0/+16
| | | | Test included for the situation formerly was not working.
* Handle case where ruby2_keywords method splats to ruby2_keywords methodJeremy Evans2019-10-241-0/+26
| | | | | | Previously, the keyword hash was duped (which results in a regular hash), but the dup was not marked as a keyword hash, causing the hash not to be marked as keyword hash even though it should be.
* Duplicate hash when converting keyword hash to keywordsJeremy Evans2019-10-241-0/+8
| | | | | | | This mirrors the behavior when manually splatting a hash. This mirrors the changes made in setup_parameters_complex in 6081ddd6e6f2297862b3c7e898d28a76b8f9240b, so that splatting to a non-iseq method works the same as splatting to an iseq method.
* Dup hash with keyword flag when converted to keywordsJeremy Evans2019-10-151-0/+8
| | | | | | | | | | | | | | | | | | | | | | | | When ruby2_keywords is used on a method, keywords passed to the method are flagged. When the hash is passed as the last element of an argument splat to another method, the hash should be treated as a keyword splat. When keyword splatting a hash, a duplicate of the hash is made. So when auto-splatting the hash with the keyword flag, a duplicate of the hash should also be made. This fixes cases where the hash is later passed to another method and would be treated as keywords there: class Object ruby2_keywords def foo(*a) bar(*a) end def bar(*a) baz(*a) end def baz(*a, **kw) [a, kw] end end foo(:a=>1) Previously, this would pass the :a=>1 as keywords to bar and also as keywords to baz. Now it only passes :a=>1 as keywords to bar, but bar passes :a=>1 as a positional hash to baz (which in this case generates a warning in 2.7).
* Allow ruby2_keywords to be used with bmethodsJeremy Evans2019-10-071-1/+32
| | | | | | | | There are libraries that use define_method with argument splats where they would like to pass keywords through the method. To more easily allow such libraries to use ruby2_keywords to handle backwards compatibility, it is necessary for ruby2_keywords to support bmethods.
* Correctly issue ArgumentError when calling method that accepts no keywordsJeremy Evans2019-09-271-0/+13
| | | | | | | If a method accepts no keywords and was called with a keyword, an ArgumentError was not always issued previously. Force methods that accept no keywords to go through setup_parameters_complex so that an ArgumentError is raised if keywords are provided.
* Fix more keyword separation issuesJeremy Evans2019-09-261-0/+648
| | | | | | | | | | | | | | | | | | | | | This fixes instance_exec and similar methods. It also fixes Enumerator::Yielder#yield, rb_yield_block, and a couple of cases with Proc#{<<,>>}. This support requires the addition of rb_yield_values_kw, similar to rb_yield_values2, for passing the keyword flag. Unlike earlier attempts at this, this does not modify the rb_block_call_func type or add a separate function type. The functions of type rb_block_call_func are called by Ruby with a separate VM frame, and we can get the keyword flag information from the VM frame flags, so it doesn't need to be passed as a function argument. These changes require the following VM functions accept a keyword flag: * vm_yield_with_cref * vm_yield * vm_yield_with_block
* Fix keyword argument separation issues in Enumerator::Generator#eachJeremy Evans2019-09-261-0/+77
| | | | This requires adding rb_proc_call_kw to pass the keyword flag.
* Fix keyword argument separation issues in Fiber#resumeJeremy Evans2019-09-261-0/+77
|
* Fix keyword argument separation issues in Thread.newJeremy Evans2019-09-261-0/+80
|
* Add Module#ruby2_keywords for passing keywords through regular argument splatsJeremy Evans2019-09-251-0/+272
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This approach uses a flag bit on the final hash object in the regular splat, as opposed to a previous approach that used a VM frame flag. The hash flag approach is less invasive, and handles some cases that the VM frame flag approach does not, such as saving the argument splat array and splatting it later: ruby2_keywords def foo(*args) @args = args bar end def bar baz(*@args) end def baz(*args, **kw) [args, kw] end foo(a:1) #=> [[], {a: 1}] foo({a: 1}, **{}) #=> [[{a: 1}], {}] foo({a: 1}) #=> 2.7: [[], {a: 1}] # and warning foo({a: 1}) #=> 3.0: [[{a: 1}], {}] It doesn't handle some cases that the VM frame flag handles, such as when the final hash object is replaced using Hash#merge, but those cases are probably less common and are unlikely to properly support keyword argument separation. Use ruby2_keywords to handle argument delegation in the delegate library.
* Make public_send and rb_f_send handle keyword argument separationJeremy Evans2019-09-231-0/+102
| | | | | Kernel#send takes a different optimized code path that was already handled.
* Handle keyword argument separation for Enumerator#sizeJeremy Evans2019-09-201-0/+99
| | | | | | | | | When Object#to_enum is passed a block, the block is called to get a size with the arguments given to to_enum. This calls the block with the same keyword flag as to_enum is called with. This requires adding rb_check_funcall_kw and rb_check_funcall_default_kw to handle keyword flags.
* Make passing empty keywords to dig pass empty keywords to next dig methodJeremy Evans2019-09-201-0/+220
| | | | | | | | | | | | | | | | If defined in Ruby, dig would be defined as def dig(arg, *rest) end, it would not use keywords. If the last dig argument was an empty hash, it could be treated as keyword arguments by the next dig method. Allow dig to pass along the empty keyword flag if called with an empty keyword, to suppress the previous behavior and force treating the hash as a positional argument and not keywords. Also handle the case where dig calls method_missing, passing the empty keyword flag to that as well. This requires adding rb_check_funcall_with_hook_kw functions, so that dig can specify how arguments are treated. It also adds kw_splat arguments to a couple static functions.
* Add and fix some keyword testsJeremy Evans2019-09-181-17/+182
| | | | | | | | | | Replace [arg=1, args] with [arg, args] so we can actually test the value correctly. Add some missing tests for **h3 when method accepts (**args). Add tests for passing positional hashes to (**args) methods and check for the expected warnings/errors.
* Fix keyword argument separation issues with sym procs when using refinementsJeremy Evans2019-09-171-0/+303
| | | | | | | | | | | | Make sure that vm_yield_with_cfunc can correctly set the empty keyword flag by passing 2 as the kw_splat value when calling it in vm_invoke_ifunc_block. Make sure calling.kw_splat is set to 1 and not 128 in vm_sendish, so we can safely check for different kw_splat values. vm_args.c needs to call add_empty_keyword, and to make JIT happy, the function needs to be exported. Rename the function to rb_adjust_argv_kw_splat to more accurately reflect what it does, and mark it as MJIT exported.
* Pass keyword argument flag when rb_call_super_kw calls method_missingJeremy Evans2019-09-171-0/+194
| | | | | | | | This makes method_missing take a flag for whether keyword arguments were passed. Adds tests both for rb_call_super_kw usage as well as general usage of super calling method_missing in Ruby methods.