[#109095] [Ruby master Misc#18888] Migrate ruby-lang.org mail services to Google Domains and Google Workspace — "shugo (Shugo Maeda)" <noreply@...>
Issue #18888 has been reported by shugo (Shugo Maeda).
16 messages
2022/06/30
[ruby-core:108883] [Ruby master Bug#18826] Symbol#to_proc inconsistent, sometimes calls private methods
From:
"jeremyevans0 (Jeremy Evans)" <noreply@...>
Date:
2022-06-14 00:12:19 UTC
List:
ruby-core #108883
Issue #18826 has been updated by jeremyevans0 (Jeremy Evans).
This appears to be caused by the use of the `VM_FCALL` flag in `vm_call_symbol`. Since `:foo.to_proc` should be treated as `lambda{|t| t.foo}`, not `lambda{|t| t.send(:foo)}`, I agree that this is a bug.
Fixing this for private methods is not too difficult, by adding a `flags` argument to `vm_call_symbol`, and only using `VM_CALL_FCALL` for the `vm_call_opt_send` case and not the `vm_invoke_symbol_block` case.
For protected methods, it's more challenging, since calling `tap` pushes a new VM frame where `cfp->self` is the receiver of `tap`, and the method called by `Symbol#to_proc` will always be called on that receiver, so protected methods would not raise exceptions. Hopefully @nobu can figure out how best to handle the protected method case.
With your second example, if you switch `private` to `protected`, you get a `NoMethodError`, which makes sense, since you have an a range receiver (`1..4`). calling an integer method (`foo`). For cases where the receiver matches, both `tap` and `collect` appear to allow protected methods:
```ruby
class Array
protected
def foo
first
end
end
a = [[1], [2], [3]]
p a.tap(&:foo)
p a.collect(&:foo)
```
So it appears to that `:foo.to_proc` should be handled the same as `lambda{|t| t.foo}` in regards to protected methods, unless we want to break backwards compatibility.
Here's a fix for the private method case:
```diff
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 09fcd2f729..33daada3cf 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -3201,7 +3201,7 @@ ci_missing_reason(const struct rb_callinfo *ci)
static VALUE
vm_call_symbol(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
- struct rb_calling_info *calling, const struct rb_callinfo *ci, VALUE symbol)
+ struct rb_calling_info *calling, const struct rb_callinfo *ci, VALUE symbol, int flags)
{
ASSUME(calling->argc >= 0);
/* Also assumes CALLER_SETUP_ARG is already done. */
@@ -3211,9 +3211,7 @@ vm_call_symbol(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
VALUE recv = calling->recv;
VALUE klass = CLASS_OF(recv);
ID mid = rb_check_id(&symbol);
- int flags = VM_CALL_FCALL |
- VM_CALL_OPT_SEND |
- (calling->kw_splat ? VM_CALL_KW_SPLAT : 0);
+ flags |= VM_CALL_OPT_SEND | (calling->kw_splat ? VM_CALL_KW_SPLAT : 0);
if (UNLIKELY(! mid)) {
mid = idMethodMissing;
@@ -3300,7 +3298,7 @@ vm_call_opt_send(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct
calling->argc -= 1;
DEC_SP(1);
- return vm_call_symbol(ec, reg_cfp, calling, calling->ci, sym);
+ return vm_call_symbol(ec, reg_cfp, calling, calling->ci, sym, VM_CALL_FCALL);
}
}
@@ -4104,7 +4102,7 @@ vm_invoke_symbol_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp,
VALUE symbol = VM_BH_TO_SYMBOL(block_handler);
CALLER_SETUP_ARG(reg_cfp, calling, ci);
calling->recv = TOPN(--calling->argc);
- return vm_call_symbol(ec, reg_cfp, calling, ci, symbol);
+ return vm_call_symbol(ec, reg_cfp, calling, ci, symbol, 0);
}
}
```
----------------------------------------
Bug #18826: Symbol#to_proc inconsistent, sometimes calls private methods
https://bugs.ruby-lang.org/issues/18826#change-97962
* Author: bjfish (Brandon Fish)
* Status: Open
* Priority: Normal
* ruby -v: 3.0.3
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
The following usage calls a protected method and prints "hello":
``` ruby
class Test
protected
def referenced_columns
puts "hello"
end
end
Test.new.tap(&:referenced_columns)
```
However, the following usage results in a NoMethodError:
``` ruby
class Integer
private
def foo
42
end
end
(1..4).collect(&:foo)
```
It seems to be a bug that tap calls a private method. It is also inconsistent with collect not calling private methods.
--
https://bugs.ruby-lang.org/
Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>