Why do variables get “defined” when they only occu

2020-07-27 16:00发布

问题:

The following ruby statement will result in x being "defined" (i.e. defined(x) returning "local-variable" even if it is undefined prior to execution of this code and even though the assignment is not performed:

x = 1 if false

Specifically, the x local variable will be set to nil. The behavior is similar for never-executed assignments subject to while false and until false clauses. You can verify this in either irb or running ruby on some code fragment.

My question is twofold:

  1. Is this behavior documented anywhere?
  2. Is there a rationale for this behavior documented anywhere?

回答1:

Here's a bit of an insight from the compiler. This is what Ruby 2.0.0 translates x = 1 if false; puts x; puts y into (before any code is executed):

== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
[ 2] x          
0000 trace            1                                               (   1)
0002 jump             7
0004 putobject_OP_INT2FIX_O_1_C_ 
0005 setlocal_OP__WC__0 2
0007 trace            1
0009 putself          
0010 getlocal_OP__WC__0 2
0012 opt_send_simple  <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP>
0014 pop              
0015 trace            1
0017 putself          
0018 putself          
0019 opt_send_simple  <callinfo!mid:y, argc:0, FCALL|VCALL|ARGS_SKIP>
0021 opt_send_simple  <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP>
0023 leave          

You can see that the local table allocates its slot 2 to the local variable x. The assignment itself (putobject, setlocal 2) is skipped over by jump 7. puts x thus fetches the slot 2 and calls puts; however, since y is not recognised as a local variable either in this scope or a higher one, it is thought it might be a method, really, almost as if you had written puts(y()). At execution time, y cannot be resolved, and the error is raised.