It seems that named references to string and array elements don't work in many contexts. I wrote the test below to determine how different types of named references behave. I describe a number of results, including the non-working references to string and array elements, after the test.
# The following test allows to assess what different types of named
# references refer to during their lifetime depending on where they
# were initialized and whether they were defined with or without the
# "-u" flag.
#
# The test defines the functions "e", "f", "g", "h", "i", "j", and
# "k". The test calls "e" and each function calls the next one. A
# string "s" and an array "a" are defined at the top-level and in each
# function before the call to the next function. The named references
# "rs", "ra", "rs1" and "ra1" are defined at the start of function "g"
# and eventually initialized with respectively "s", "a", "s[1]", and
# "a[1]". Each function "g", "h", "i", "j", and "k" prints the
# expansion of the named references before the local definition of "s"
# and "a", after that definition but before the call to the next
# function and after that call.
#
# Usage: nr-test.zsh (-u|"") <0-12>
#
# The first parameter determines whether the named references are
# defined with or without the flag "-u".
#
# The second parameter determines where the named references are
# initialized. In all cases the named references are defined at the
# start of function "g". With value "0" they are initialized at the
# same place (in the same statement). With the other values, the
# initialization is delayed until later, the greater the value and the
# later the initialization.
function e() {
local s=$0 a=($0);
f "$@";
}
function f() {
local s=$0 a=($0);
g "$@";
}
function g() {
if (($2)); then local -n $1 rs ra rs1 ra1; else local -n $1 rs=s ra=a rs1="s[1]" ra1="a[1]"; fi;
if (($2 == 1)); then rs=s; ra=a; rs1="s[1]"; ra1="a[1]"; fi;
echo "$0:1: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
local s=$0 a=($0);
if (($2 == 2)); then rs=s; ra=a; rs1="s[1]"; ra1="a[1]"; fi;
echo "$0:2: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
h "$@";
if (($2 == 12)); then rs=s; ra=a; rs1="s[1]"; ra1="a[1]"; fi;
echo "$0:3: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
}
function h() {
if (($2 == 3)); then rs=s; ra=a; rs1="s[1]"; ra1="a[1]"; fi;
echo "$0:1: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
local s=$0 a=($0);
if (($2 == 4)); then rs=s; ra=a; rs1="s[1]"; ra1="a[1]"; fi;
echo "$0:2: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
i "$@";
if (($2 == 11)); then rs=s; ra=a; rs1="s[1]"; ra1="a[1]"; fi;
echo "$0:3: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
}
function i() {
if (($2 == 5)); then rs=s; ra=a; rs1="s[1]"; ra1="a[1]"; fi;
echo "$0:1: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
local s=$0 a=($0);
if (($2 == 6)); then rs=s; ra=a; rs1="s[1]"; ra1="a[1]"; fi;
echo "$0:2: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
j "$@";
if (($2 == 10)); then rs=s; ra=a; rs1="s[1]"; ra1="a[1]"; fi;
echo "$0:3: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
}
function j() {
if (($2 == 7)); then rs=s; ra=a; rs1="s[1]"; ra1="a[1]"; fi;
echo "$0:1: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
local s=$0 a=($0);
if (($2 == 8)); then rs=s; ra=a; rs1="s[1]"; ra1="a[1]"; fi;
echo "$0:2: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
k "$@";
if (($2 == 9)); then rs=s; ra=a; rs1="s[1]"; ra1="a[1]"; fi;
echo "$0:3: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
}
function k() {
echo "$0:1: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
}
(($# == 2)) || { echo "Usage: $ZSH_SCRIPT (-u|\"\") <0-11>" 1>&2; exit 1; }
local s=T a=(T);
e "$@";
Let's start with named references defined with the "-u" flag, where things at least are consistent. Named references to the string and to the array systematically refer to the definitions in the enclosing scope of the scope at which the expansion takes place. It's unclear to me whether that's the intended behavior but I very much hope that it's NOT. Named references to the string and to the array elements systematically refer to the definition at the top-level, which certainly is a bug.
% zsh-5.9.0.2-test nr-test.zsh -u 0
g:1: rs=f - ra=f - rs1=T - ra1=T
g:2: rs=f - ra=f - rs1=T - ra1=T
h:1: rs=g - ra=g - rs1=T - ra1=T
h:2: rs=g - ra=g - rs1=T - ra1=T
i:1: rs=h - ra=h - rs1=T - ra1=T
i:2: rs=h - ra=h - rs1=T - ra1=T
j:1: rs=i - ra=i - rs1=T - ra1=T
j:2: rs=i - ra=i - rs1=T - ra1=T
k:1: rs=j - ra=j - rs1=T - ra1=T
j:3: rs=i - ra=i - rs1=T - ra1=T
i:3: rs=h - ra=h - rs1=T - ra1=T
h:3: rs=g - ra=g - rs1=T - ra1=T
g:3: rs=f - ra=f - rs1=T - ra1=T
The outputs for greater values of the second parameter are all similar to the one above.
When the named references are defined without the "-u" flag, things are more complicated. Let's first look at the case where the references are defined and initialized in one single statement at the start of function "g":
% zsh-5.9.0.2-test nr-test.zsh "" 0
g:1: rs=f - ra=f - rs1=T - ra1=T
g:2: rs=f - ra=f - rs1=T - ra1=T
h:1: rs=f - ra=f - rs1=T - ra1=T
h:2: rs=f - ra=f - rs1=T - ra1=T
i:1: rs=f - ra=f - rs1=T - ra1=T
i:2: rs=f - ra=f - rs1=T - ra1=T
j:1: rs=f - ra=f - rs1=T - ra1=T
j:2: rs=f - ra=f - rs1=T - ra1=T
k:1: rs=f - ra=f - rs1=T - ra1=T
j:3: rs=f - ra=f - rs1=T - ra1=T
i:3: rs=f - ra=f - rs1=T - ra1=T
h:3: rs=f - ra=f - rs1=T - ra1=T
g:3: rs=f - ra=f - rs1=T - ra1=T
Here, the named references to the string and the array behave as expected, while the ones to the string and the array elements exhibit the same bug as with the flag "-u". That bug can also be observed with the second parameter values 1, 3, 5, and 7. In other words, the bug appears each time the references are initialized in a context where the referred variable can't be found in the scope in which the initialization takes place. For all the other values of the second parameter the references to the string and the array elements behave exactly the same as the references to the string and the array.
For the second parameter value 1, which defines and initializes the references in two steps instead of a single one, the result is as expected the same as above. For the value 2 and 3, the references systematically refer to the variables defined in function "g", which is expected since they are initialized just after these variables are defined:
% zsh-5.9.0.2-test nr-test.zsh "" 2
g:1: rs= - ra= - rs1= - ra1=
g:2: rs=g - ra=g - rs1=g - ra1=g
h:1: rs=g - ra=g - rs1=g - ra1=g
h:2: rs=g - ra=g - rs1=g - ra1=g
i:1: rs=g - ra=g - rs1=g - ra1=g
i:2: rs=g - ra=g - rs1=g - ra1=g
j:1: rs=g - ra=g - rs1=g - ra1=g
j:2: rs=g - ra=g - rs1=g - ra1=g
k:1: rs=g - ra=g - rs1=g - ra1=g
j:3: rs=g - ra=g - rs1=g - ra1=g
i:3: rs=g - ra=g - rs1=g - ra1=g
h:3: rs=g - ra=g - rs1=g - ra1=g
g:3: rs=g - ra=g - rs1=g - ra1=g
With values 4, 5, 6, and 7, we can observe that the referred variables in function "j" change after the call to function "k" ("j:3" is different from "j:2"). With values 4 and 5, the same is true in the function "i" after the call to function "j" ("i:3" is different from "i:2"):
% zsh-5.9.0.2-test nr-test.zsh "" 4
g:1: rs= - ra= - rs1= - ra1=
g:2: rs= - ra= - rs1= - ra1=
h:1: rs= - ra= - rs1= - ra1=
h:2: rs=h - ra=h - rs1=h - ra1=h
i:1: rs=h - ra=h - rs1=h - ra1=h
i:2: rs=h - ra=h - rs1=h - ra1=h
j:1: rs=h - ra=h - rs1=h - ra1=h
j:2: rs=h - ra=h - rs1=h - ra1=h
k:1: rs=h - ra=h - rs1=h - ra1=h
j:3: rs=j - ra=j - rs1=j - ra1=j
i:3: rs=i - ra=i - rs1=i - ra1=i
h:3: rs=h - ra=h - rs1=h - ra1=h
g:3: rs=g - ra=g - rs1=g - ra1=g
To sum up, there seems to be at least two bugs:
- The named references to string and array elements which refer to global variables.
- The named references that get rebound after a function call.
I also hope that the current behavior of named references defined with the "-u" is a bug and not the expected behavior.
Philippe