From: "ko1 (Koichi Sasada) via ruby-core" <ruby-core@...>
Date: 2024-07-11T07:55:53+00:00
Subject: [ruby-core:118551] [Ruby master Bug#20626] `defined?(@ivar)` should return nil when `@iv` can raise on Ractor

Issue #20626 has been updated by ko1 (Koichi Sasada).


On the devmeeting, akr pointed that `@iv = foo unless defined?(@iv)` isn't be supported with Case1's proposal.

So

* 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.

----------------------------------------
Bug #20626: `defined?(@ivar)` should return nil when `@iv` can raise on Ractor
https://bugs.ruby-lang.org/issues/20626#change-109072

* Author: ko1 (Koichi Sasada)
* Status: Open
* Assignee: ko1 (Koichi Sasada)
* Backport: 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN
----------------------------------------
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 more modification to YJIT.




-- 
https://bugs.ruby-lang.org/
 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/