Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Issues with references to subscripted variables



References to subscripted variables (e.g., local -n ref=var[i]) are affected by the two issues described below. They render the usage of such references very brittle. So much so, that in my opinion such references should NOT be supported in the next release unless the issues can be addressed.

Issue 1: Multiple subscripts

References and multiple subscripts don't mix well. Here is a very straightforward example:

$ arr=(X abc X); local -n ref=arr[2][3]; echo "A:$ref"; echo "B:${ref}"
A:abc[3]
B:c

The issue occurs only with the syntax $name, not with ${name}. The expansion of references kind of simply textually replaces references with their referent and then expands the resulting string. Thus, $ref expands to $arr[2][3], which then expands to abc[3] because the syntax $name only supports a single subscript. ${ref} expands to ${arr[2][3]}, which then expands to c because the syntax ${name} supports any number of subscripts.

Forbidding multiple subscripts in reference definitions doesn't avoid the issue because it also occurs when subscripts from different expressions combine:

$ arr=(X abc X); local -n ref=arr[2]; echo "A:$ref[3]"; echo "B:${ref[3]}"
A:abc[3]
B:c

The issue is avoided if references are only ever expanded with the syntax ${name}. However, unless this is enforced, which we probably don't want to do, this would certainly be very error prone as it's just too easy to forget if you are used to using the short syntax for variable expansions. Furthermore, it only avoids the issue for expansions but the issue also affects assignments:

$ arr=(X abc X); local -n ref=arr[2][3]; ref=Z; echo "A:$arr"
A:X Z X
$ arr=(X abc X); local -n ref=arr[2]; ref[3]=Z; echo "A:$arr"
A:X Z X

In these examples, ref=Z and ref[3]=Z first expand to arr[3][2]=Z, which isn't a valid assignment _expression_; Zsh supports at most one subscript in assignments. Explicitly evaluating the assignment arr[3][2]=Z generates an error but when it occurs internally, like in the examples above, the additional subscript is simply ignored.

Afaik, there is no workaround to make this work. The syntax ${(A)name::=word} is of no help.

Issue 2: reference to subscripted reference

When a reference A references a subscripted referent B that is itself a reference, expansions of A wrongly apply the subscript to the content of B (i.e., the name of the variable referred by B) instead of applying it to the content of the variable referred by B:

$ var=XYZ; local -n ref1=var ref2=ref1[2]; echo ref2=$ref2
ref2=a
$ arr=(X Y Z); local -n ref1=arr ref2=ref1[2]; echo ref2=$ref2
ref2=r

Assignments to A wrongly modify the content of B instead of modifying the content of the variable referred by B:

$ var=XYZ; local -n ref1=var ref2=ref1[2]; ref2=abc; typeset -p var ref1
typeset -g var=XYZ
typeset -n ref1=vabcr
$ arr=(X Y Z); local -n ref1=arr ref2=ref1[2]; ref2=abc; typeset -p arr ref1
typeset -g -a arr=( X Y Z )
typeset -n ref1=aabcr

In both cases, the reason is, as Bart commented in workers/54076, because resolve_nameref() (wrongly) assumes that a subscripted referent must be the stopping point, and so returns its input.

Discussion

The examples above are unlikely to occur as such in any real script. However, it's not difficult to come up with more real life scenarios that trigger the same issues; references to subscripted variables look very brittle to me. It's not far-fetched to think that many users who try to use them would be affected by at least one of these issues.

I think that it might be possible to fix issue 2 as well as the expansions of issue 1 without too many changes in the code. However, I don't think that it's possible to fix the assignments of issue 1 without huge changes, which look ill advised to me just before a new release.

If assignments can't be fixed, references to subscripted variables would remain brittle. Therefore, I would rather not include references to subscripted variables in the next release and instead only support references to plain variables. Support for references to subscripted variables could still be added in a subsequent release.

Tests

The attached file contains extensive tests of expansions of references to subscripted variables. The tests, as written, pass but each output line that starts with KO signals a bogus expansion. The result in every output line in each block should be the same as the result in the first line in the same block.

Philippe

Attachment: K08nameref-subscripts.ztst
Description: Binary data



Messages sorted by: Reverse Date, Date, Thread, Author