Bug #20626
closed`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.
Updated by ko1 (Koichi Sasada) 10 months ago
ยท Edited
On the devmeeting, akr pointed that @iv = foo unless defined?(@iv)
isn't be supported with Case1's proposal.
So the following approach was approved by matz:
- On Case1 returns "instance-variable" because it is defined (but error when accsses).
- On Case1 returns
nil
because it seems simple bug.
I'll remake a patch.
Updated by Eregon (Benoit Daloze) 10 months ago
AFAIK defined?(expr)
checks whether the expression is defined, e.g. if it's a variable whether there is such a variable.
That doesn't change with Ractor, there is in fact an @iv1
variable and it is observable because reading @does_not_exist
in a Ractor would work (and just return nil
).
So I think it is not good to change defined?(@ivar)
for Ractor, it is about whether something exists, but defined?(expr)
never guaranteed expr
will not raise.
Updated by jeremyevans0 (Jeremy Evans) 10 months ago
- Status changed from Open to Closed