Project

General

Profile

Bug #22235

Updated by Eregon (Benoit Daloze) 2 months ago

```ruby 
 namespace = Module.new 
 begin 
   namespace::NotDefined += 1 
 rescue => e 
   pp e.backtrace_locations[0] 
   pp e.backtrace_locations[0].source_range 
   pp e.backtrace_locations[0].syntax_tree 
 end 
 ``` 

 On CRuby master it gives: 
 ``` 
 $ ruby -v const_assignment_fail_while_reading.rb 
 ruby 4.1.0dev (2026-08-08T08:04:35Z master e56f452e46) +PRISM [x86_64-linux] 
 "const_assignment_fail_while_reading.rb:3:in '<main>'" 
 #<Ruby::SourceRange /.../const_assignment_fail_while_reading.rb:(3,2)-(3,28)> 
 @ ConstantPathOperatorWriteNode (location: (3,2)-(3,28)) 
 ├── flags: newline 
 ├── target: 
 │     @ ConstantPathNode (location: (3,2)-(3,23)) 
 │     ├── flags: ∅ 
 │     ├── parent: 
 │     │     @ LocalVariableReadNode (location: (3,2)-(3,11)) 
 │     │     ├── flags: ∅ 
 │     │     ├── name: :namespace 
 │     │     └── depth: 0 
 │     ├── name: :NotDefined 
 │     ├── delimiter_loc: (3,11)-(3,13) = "::" 
 │     └── name_loc: (3,13)-(3,23) = "NotDefined" 
 ├── binary_operator_loc: (3,24)-(3,26) = "+=" 
 ├── value: 
 │     @ IntegerNode (location: (3,27)-(3,28)) 
 │     ├── flags: static_literal, decimal 
 │     └── value: 1 
 └── binary_operator: :+ 
 ``` 

 So even though it's the read failing, it returns the `ConstantPathOperatorWriteNode` and the section is `namespace::NotDefined += 1`. 

 I'm implementing `source_range` & `syntax_tree` on TruffleRuby and there the result is: 
 ``` 
 "const_assignment_fail_while_reading.rb:3:in 'Module#const_missing'" 
 #<Ruby::SourceRange /.../const_assignment_fail_while_reading.rb:(3,2)-(3,23)> 
 @ ConstantPathNode (location: (3,2)-(3,23)) 
 ... 
 ``` 

 So this returns the `ConstantPathNode` and the section is `namespace::NotDefined`. 

 I think TruffleRuby is correct here, especially if we want to differentiate failing while reading or writing. 
 On CRuby it is currently impossible to differentiate because the same node is returned in both cases. 

 More information: 
 ``` 
 irb(main):001> namespace = Module.new 
 irb(main):002> namespace::NotDefined += 1 
 irb(main):003> e=_ 
 irb(main):005> l=e.backtrace_locations[0] 
 irb(main):008> puts RubyVM::InstructionSequence.of(l).disasm 
 == disasm: #<ISeq:<compiled>@(irb):2 (2,0)-(2,26)> 
 0000 getlocal_WC_1                            namespace@0                 (     2)[Li] 
 0002 dup 
 0003 putobject                                true 
 0005 getconstant                              :NotDefined 
 0007 putobject_INT2FIX_1_ 
 0008 opt_plus                                 <calldata!mid:+, argc:1, FCALL|ARGS_SIMPLE>[CcCr] 
 0010 swap 
 0011 topn                                     1 
 0013 swap 
 0014 setconstant                              :NotDefined 
 0016 leave 
 irb(main):011> RubyVM::InstructionSequence.of(l).to_a 
 =>  
 ["YARVInstructionSequence/SimpleDataFormat", 
  4, 
  1, 
  1, 
  {arg_size: 0, 
   local_size: 0, 
   stack_max: 3, 
   node_id: 6, 
   source_hash: 614720579064667961, 
   code_location: [2, 0, 2, 26], 
   node_ids: [2, 5, 5, 5, 4, 5, 5, 5, 5, 5, 6], 
   parser: :prism}, 
  "<compiled>", 
  "(irb)", 
  nil, 
  2, 
  :eval, 
  [], 
  {}, 
  [], 
  [2, 
   :RUBY_EVENT_LINE, 
   [:getlocal_WC_1, 3], 
   [:dup], 
   [:putobject, true], 
   [:getconstant, :NotDefined], 
   [:putobject_INT2FIX_1_], 
   [:opt_plus, {mid: :+, flag: 20, orig_argc: 1}], 
   [:swap], 
   [:topn, 1], 
   [:swap], 
   [:setconstant, :NotDefined], 
   [:leave]]] 
 irb(main):013> l.syntax_tree 
 =>  
 @ ConstantPathOperatorWriteNode (location: (1,0)-(1,26)) 
 ... 
 ``` 

 The failing instruction must be `getconstant` (BTW, do we have any API to get the insn/insn insn index for an exception?). 
 That's given `node_id` 5 which is the `ConstantPathOperatorWriteNode`. 

 OK to fix the `node_id` of `getconstant` in such a case to point to the `ConstantPathNode`?

Back