Project

General

Profile

Actions

Bug #22235

closed

node_id/location for constant assignment failing while reading

Bug #22235: node_id/location for constant assignment failing while reading

Added by Eregon (Benoit Daloze) about 3 hours ago. Updated about 2 hours ago.

Status:
Rejected
Assignee:
-
Target version:
-
[ruby-core:<unknown>]

Description

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.
(UPDATE: it's possible to differentiate via the exception, but that seems quite brittle. The point is syntax_tree&source_range should point to the relevant reading node)

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 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?

Actions

Also available in: PDF Atom