Bug #20626
Updated by ko1 (Koichi Sasada) 7 days ago
Return `nil` for `defined?(@ivar)` on some cases with Ractor.
## Background
It is not allowed to get instance variables from an object if
1. the object is shareable
2. and the instance variable refers to an unshareable object
like the following code:
```ruby
class Ractor
def setup_iv
@iv = []
end
def get_iv = @iv
end
R = Ractor.new{}
R.setup_iv
p R.get_iv #=> []
Ractor.new{|;iv|
p R.get_iv #=> can not access instance variables of shareable objects from non-main Ractors (Ractor::IsolationError)
}.take
```
I believe nobody use ivars on Ractor objects. However it is used on classes and modules (shareable objects) casually use it.
```ruby
class C
@iv = []
def self.iv = @iv
end
p C.iv #=> []
Ractor.new{
p C.iv #=> can not get unshareable values from instance variables of classes/modules from non-main Ractors (Ractor::IsolationError)
}.take
```
Current behavior on `defined?` with such ivars is strange because:
* Case1: raising an error
```ruby
class C
@iv1 = []
def self.defined_iv1 = defined?(@iv1)
end
Ractor.new{
p C.defined_iv1
#=> can not get unshareable values from instance variables of classes/modules from non-main Ractors (Ractor::IsolationError)
}.take
```
* Case2: incorrect result
```ruby
class C
# @iv2 is not defined
def self.defined_iv2 = defined?(@iv2)
end
Ractor.new{
p C.defined_iv2 #=> "instance-variable"
}.take
```
This is because current implementation uses accessing ivars on such cases. It seems a simple bug.
## Proposal
In other words, we can't use such ivars on Ractors on Case1.
So that returning `nil` on Case1 and Case2 reasonable.
```ruby
class C
@iv1 = []
def self.defined_iv1 = defined?(@iv1)
# @iv2 is not defined
def self.defined_iv2 = defined?(@iv2)
@iv3 = 42 # refers to shareable object
def self.defined_iv2 = defined?(@iv3)
end
p C.defined_iv1 #=> "instance-variable"
p C.defined_iv2 #=> "instance-variable"
Ractor.new{
p C.defined_iv1
# current: can not get unshareable values from instance variables of classes/modules from non-main Ractors (Ractor::IsolationError)
# proposed: nil because it is not accessible from here
p C.defined_iv2
# current: "instance-variable"
# proposed: nil
p C.defined_iv3 #=> "instance-variable" because we can access it
}.take
```
## Usage
`FileUtils` uses `defined?(@non_existing_ivar)` technique to configure its behavior so I found the Case2.
https://github.com/ruby/fileutils/blob/master/lib/fileutils.rb#L2501
## Implementation
https://github.com/ruby/ruby/pull/11141
We need to more modification to YJIT.