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

Issues with named references



Hi everyone,

Following Eric's announcement about the test release, I finally took the time to better understand the newly introduced named references. I had loosely followed the discussions while they were being designed and implemented. Unfortunately, it's only now that I found the time to test them.

My assessment is that named references don't work as I had imagined that they would. More worryingly, I can't figure out how they are supposed to work. One issue is that my understanding of the documentation doesn't seem to match what I observe in my tests. Furthermore, even though I don't fully understand how the implemented named references are supposed to work, I think that I have found an example that exhibits a bug.

An important expectation that I have for named references is that once a named reference is successfully resolved, it keeps referring to the same variable for the rest of its lifetime. A corollary is that a named reference can only refer to a variable in the same scope as or in an enclosing scope of the scope in which it is defined. Indeed, if a named reference refers to a variable in a more deeply nested scope, then this reference becomes a dangling one once that scope comes to an end. The creation of dangling references implies that previously valid named references either become invalid or are rebound to different variables.

One way to ensure that a successfully resolved named reference never again becomes invalid nor is ever rebound to a different variable is to always resolve it at the scope in which it was defined (i.e., at the scope in which "typedef -n pname" was called, or, for "typedef -g -n pname", at the scope where "pname" existed when "typedef" was called or at the global scope if no "pname" existed). That's not what was implemented. Section 14.3.2 states instead that "named references always expand parameters at the scope in which rname existed when ‘typeset -n’ was called". For a declaration like "typeset -n pname=rname" where "rname" already exists, the two specifications are equivalent. However, the description of "typedef" states "The second parameter (i.e., rname) need not exist at the time the reference is created" and section 15.5 states "The first non-empty assignment to pname initializes the reference", which suggests that "typedef -n pname" with no "rname" are allowed. These two features are both incompatible with the statement of section 14.3.2 (it's unspecified how such named references are supposed to be resolved). My tests show that they both enable the creation of dangling references, which lead to previously successfully resolved named references becoming invalid again and being rebound to new variables:

dangling-references.zsh:
function f1() {
  typeset -n ref1;
  typeset -n ref2=var;
  f2;
  echo "$0: ref1=$ref1 - ref2=$ref2";
  f3;
  echo "$0: ref1=$ref1 - ref2=$ref2";
}

function f2() {
  local var=$0;
  ref1=var;
  echo "$0: ref1=$ref1 - ref2=$ref2";
}

function f3() {
  local var=$0;
  echo "$0: ref1=$ref1 - ref2=$ref2";
}

f1;

Output:
f2: ref1=f2 - ref2=f2
f1: ref1= - ref2=
f3: ref1=f3 - ref2=f3
f1: ref1= - ref2= 

For calls to "typedef -n pname=rname" where "rname" doesn't exist yet, the implementation seems to resolve "rname" at expansion time at the outermost scope where "rname" exists. I would have expected the same for calls to "typedef -n pname" later followed by a call to "pname=rname" but that's not the case:

delayed-named-reference-initialization.zsh:
function f1() {
  typeset -n ref;
  f2;
}

function f2() {
  local var=$0;
  ref=var;
  echo "$0: ref=$ref";
  f3;
  echo "$0: ref=$ref";
}

function f3() {
  echo "$0: ref=$ref";
  local var=$0;
  echo "$0: ref=$ref";
  f4;
  echo "$0: ref=$ref";
}

function f4() {
  echo "$0: ref=$ref";
  local var=$0;
  echo "$0: ref=$ref";
  f5;
  echo "$0: ref=$ref";
}

function f5() {
  echo "$0: ref=$ref";
}

f1;

Output:
f2: ref=f2
f3: ref=f2
f3: ref=f2
f4: ref=f2
f4: ref=f2
f5: ref=f2
f4: ref=f4
f3: ref=f3
f2: ref=f2 

I can't explain the second and third to last lines. These look like bugs to me. Both become "ref=f2" if either "ref" is initialized to "var" in "f1", or if the first two lines of "f2" are switched.


Another important expectation that I have for named references is that functions have a failsafe way to define named references to variables in their enclosing scope (i.e., their caller's scope). I thought that the -u flag was meant for that and that's also what the second part of section 14.3.2 suggests. However, the -u flag seems to completely change the way named references are resolved:

named-references-with-u-flag.zsh:
function f1() {
  local var=$0;
  f2
}

function f2() {
  typeset -n -u ref=var;
  echo "$0: ref=$ref";
  local var=$0;
  echo "$0: ref=$ref";
  f3
  echo "$0: ref=$ref";
}

function f3() {
  echo "$0: ref=$ref";
  local var=$0;
  echo "$0: ref=$ref";
  f4
  echo "$0: ref=$ref";
}

function f4() {
  echo "$0: ref=$ref";
}

f1;

Output:
f2: ref=f1
f2: ref=f1
f3: ref=f2
f3: ref=f2
f4: ref=f3
f3: ref=f2
f2: ref=f1

My expectation was that all lines would be "ref=f1", which is what I get, as expected, if I remove the -u flag. With an extra "local var=$0;" at the start of "f2", I also expected that all lines would be "ref=f1", while without the -u flag they are, as expected, "ref=f2", but instead I get the same output as above. Apparently the effect of the -u flag is to always resolve rname in the enclosing scope of the scope where the expansion of pname takes place, which is fundamentally different from "named references always expand parameters at the scope in which rname existed when ‘typeset -n’ was called". It's unclear to me how and where this flag could be useful.


I had high hopes for named references but at the moment it's unclear to me whether the current implementation is bringing something useful. The documentation looks incomplete to me and my tests weren't enough to let me fully understand how named references work.

Philippe


Attachment: dangling-references.zsh
Description: Binary data

Attachment: delayed-named-reference-initialization.zsh
Description: Binary data

Attachment: named-references-with-u-flag.zsh
Description: Binary data



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