Bug #20626
open`defined?(@ivar)` should return nil when `@iv` can raise on Ractor
Description
Return nil
for defined?(@ivar)
on some cases with Ractor.
Background¶
It is not allowed to get instance variables from an object if
- the object is shareable
- and the instance variable refers to an unshareable object
like the following code:
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.
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
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
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.
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 more modification to YJIT.