[ruby-core:111451] [Ruby master Bug#19269] Constant lookup and #instance_eval

Issue #19269 has been reported by andrykonchin (Andrew Konchin). ---------------------------------------- Bug #19269: Constant lookup and #instance_eval https://bugs.ruby-lang.org/issues/19269 * Author: andrykonchin (Andrew Konchin) * Status: Open * Priority: Normal * ruby -v: 3.1.3 * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN ---------------------------------------- I've noticed a confusing behaviour of `#instance_eval` (and `#instance_exec` as well). In some cases it doesn't see constants defined in the object class. Examples: ```ruby C = 1 class A C = 2 end ``` When `#instance_eval` is called with a String - `A::C` constant is visible, that is pretty expected: ```ruby A.new.instance_eval("C") # => 2 ``` But when it's called with a block - `A::C` isn't visible: ```ruby A.new.instance_eval { C } # => 1 ``` If we define a method that returns a constant (defined in the class), then `A::C` is visible in both cases: ```ruby C = 1 class A C = 2 def c; C; end end A.new.instance_eval("c") # => 2 A.new.instance_eval { c } # => 2 ``` So we see that when `#instance_eval` called with a block and a constant is assessed directly is the only case when a class constant isn't visible. Wondering whether it's an expected behaviour and the reason to behave this way. -- https://bugs.ruby-lang.org/

Issue #19269 has been updated by zverok (Victor Shepelev). As far as I understand, name lookup in block is performed related to the context where the block is defined. The example with `c` (local name) works, because in `instance_eval`, it is implicitly `self.c`. But wouldn't work if you'll shadow the name with local variable: ```ruby C = 1 class A C = 2 def c; C; end end c = 1 A.new.instance_eval("c") # => 2 A.new.instance_eval { c } # => 1 ``` The block could've come from completely different context, and the block is "closure": it remembers the context from where it came from. It is not specific to `instance_eval`, that's just how the blocks work. (With `instance_eval(string)`, there is no contexts it carries with itself, so it is interpreted in the target context.) This would work unambiguously (but is tedious to write: ```ruby C = 1 class A C = 2 def c; C; end end c = 1 A.new.instance_eval { self.c } # => 2 A.new.instance_eval { self.class::C } # => 2 ``` ---------------------------------------- Bug #19269: Constant lookup and #instance_eval https://bugs.ruby-lang.org/issues/19269#change-100819 * Author: andrykonchin (Andrew Konchin) * Status: Open * Priority: Normal * ruby -v: 3.1.3 * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN ---------------------------------------- I've noticed a confusing behaviour of `#instance_eval` (and `#instance_exec` as well). In some cases it doesn't see constants defined in the object class. Examples: ```ruby C = 1 class A C = 2 end ``` When `#instance_eval` is called with a String - `A::C` constant is visible, that is pretty expected: ```ruby A.new.instance_eval("C") # => 2 ``` But when it's called with a block - `A::C` isn't visible: ```ruby A.new.instance_eval { C } # => 1 ``` If we define a method that returns a constant (defined in the class), then `A::C` is visible in both cases: ```ruby C = 1 class A C = 2 def c; C; end end A.new.instance_eval("c") # => 2 A.new.instance_eval { c } # => 2 ``` So we see that when `#instance_eval` called with a block and a constant is assessed directly is the only case when a class constant isn't visible. Wondering whether it's an expected behaviour and the reason to behave this way. -- https://bugs.ruby-lang.org/

Issue #19269 has been updated by Eregon (Benoit Daloze). Status changed from Open to Closed This is expected because constant lookup is lexicial, i.e., it's only changed by `module/class` keywords, and by eval'ed strings. ---------------------------------------- Bug #19269: Constant lookup and #instance_eval https://bugs.ruby-lang.org/issues/19269#change-100993 * Author: andrykonchin (Andrew Konchin) * Status: Closed * Priority: Normal * ruby -v: 3.1.3 * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN ---------------------------------------- I've noticed a confusing behaviour of `#instance_eval` (and `#instance_exec` as well). In some cases it doesn't see constants defined in the object class. Examples: ```ruby C = 1 class A C = 2 end ``` When `#instance_eval` is called with a String - `A::C` constant is visible, that is pretty expected: ```ruby A.new.instance_eval("C") # => 2 ``` But when it's called with a block - `A::C` isn't visible: ```ruby A.new.instance_eval { C } # => 1 ``` If we define a method that returns a constant (defined in the class), then `A::C` is visible in both cases: ```ruby C = 1 class A C = 2 def c; C; end end A.new.instance_eval("c") # => 2 A.new.instance_eval { c } # => 2 ``` So we see that when `#instance_eval` called with a block and a constant is accessed directly is the only case when a class constant isn't visible. Wondering whether it's an expected behaviour and the reason to behave this way. --- In the examples above I've added a top-level declaration `C=1` only for readability. Without this declaration in all the cases when `C` value `1` is returned - a NoName exception is raised (`uninitialized constant C (NameError)`) -- https://bugs.ruby-lang.org/

Issue #19269 has been updated by andrykonchin (Andrew Konchin). @zverok Thank you for clarification 🙇. ---------------------------------------- Bug #19269: Constant lookup and #instance_eval https://bugs.ruby-lang.org/issues/19269#change-100996 * Author: andrykonchin (Andrew Konchin) * Status: Closed * Priority: Normal * ruby -v: 3.1.3 * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN ---------------------------------------- I've noticed a confusing behaviour of `#instance_eval` (and `#instance_exec` as well). In some cases it doesn't see constants defined in the object class. Examples: ```ruby C = 1 class A C = 2 end ``` When `#instance_eval` is called with a String - `A::C` constant is visible, that is pretty expected: ```ruby A.new.instance_eval("C") # => 2 ``` But when it's called with a block - `A::C` isn't visible: ```ruby A.new.instance_eval { C } # => 1 ``` If we define a method that returns a constant (defined in the class), then `A::C` is visible in both cases: ```ruby C = 1 class A C = 2 def c; C; end end A.new.instance_eval("c") # => 2 A.new.instance_eval { c } # => 2 ``` So we see that when `#instance_eval` called with a block and a constant is accessed directly is the only case when a class constant isn't visible. Wondering whether it's an expected behaviour and the reason to behave this way. --- In the examples above I've added a top-level declaration `C=1` only for readability. Without this declaration in all the cases when `C` value `1` is returned - a NoName exception is raised (`uninitialized constant C (NameError)`) -- https://bugs.ruby-lang.org/
participants (3)
-
andrykonchin (Andrew Konchin)
-
Eregon (Benoit Daloze)
-
zverok (Victor Shepelev)