Attached is a patch containing: 1) The changes previously diff'd in workers/53560, which is superseded 2) The fix for chained references mentioned in workers/53567, which has a conflict with 53560 3) The documentation update mentioned in workers/53557 and workers/53567 Also attached is the entire K01nameref.ztst file, which has extensive additions cribbed from the foregoing and from workers/53546 (some of this has already been pushed). I'm going to push this shortly because one of Philippe's asserted TODO is to re-test against the latest version.
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index 7eade4a11..1db65d24f 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -1560,7 +1560,14 @@ ifzman() indent(tt(typeset -n )var(pname)tt(=)var(rname)) initializes a parameter var(pname) as a reference to a second -parameter var(rname). With the few exceptions described here, when +parameter var(rname). The var(rname) may also be omitted, in which +case var(pname) is a placeholder which, the first time it is assigned, +is initialized to an active reference to the assigned var(rname). See +ifzman(Named References in zmanref(zshparam))\ +ifnzman(noderef(Named References) under noderef(Parameters)) +for more about placeholders. + +With the few exceptions described here, when var(pname) is used in any of the expansion forms described above, the parameter var(rname) is expanded instead. This is similar to the action of the `tt((P))' expansion flag, but when var(rname) has itself @@ -1577,11 +1584,11 @@ This includes arrays declared, but not initialized, when the option tt(TYPESET_TO_UNSET) is in effect. The var(word) is substituted but no assignment occurs. -Also unlike `tt((P))' named references always expand parameters at -the scope in which var(rname) existed when `tt(typeset -n)' was -called. This can be used to expand or assign parameters from an -earlier scope even if a local of the same name has been declared at -a later scope. Example: +Also unlike `tt((P))' named references always expand parameters at the +scope in which var(rname) exists when var(pname) is initialized. This +can be used to expand or assign parameters from an earlier scope even +if a local of the same name has been declared at a later scope. +Example: ifzman() example(tt(caller=OUTER) tt(func LPAR()RPAR() {) @@ -1600,12 +1607,13 @@ example(tt(before local: OUTER) tt(by reference: OUTER) tt(after func: RESULT)) -To force a named reference to refer to the outer scope, even if a local -has already been declared, add the tt(-u) option when declaring the -named reference. In this case var(rname) should already exist in the -outer scope, otherwise the behavior of assignment through var(pname) -is not defined and may change the scope of the reference or fail with -a status of 1. Example of correct usage: +To force a named reference to refer to the outer scope, even if a +local has already been declared, add the tt(-u) option when declaring +the named reference. In this case var(rname) should already exist in +the outer scope before var(pname) is initialized, otherwise the +behavior of assignment through var(pname) is not defined and may +change the scope of the reference or fail with a status of 1. Example +of correct usage: ifzman() example(tt(caller=OUTER) tt(func LPAR()RPAR() {) @@ -1623,6 +1631,13 @@ Note, however, that named references to em(special) parameters acquire the behavior of the special parameter, regardless of the scope where the reference is declared. +In the event that the local var(pname) goes out of scope (its declaring +function returns) before the reference var(rname) goes out of scope, +the reference may change to another parameter having the same name as +var(pname), or assignments may fail as described above. Keep the +declaration of var(rname) as close as possible to its initialization +to var(pname) to avoid confusion. + When var(rname) includes an array subscript, the subscript expression is interpreted at the time tt(${)var(pname)tt(}) is expanded. Any form of subscript is allowed, including those that select individual diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index 69298855f..c52b6ba91 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -694,6 +694,15 @@ assignments to, var(pname) act on the referenced parameter. This is explained in the Named References section of ifzman(zmanref(zshexpn))ifnzman(noderef(Parameter Expansion)). +A placeholder var(pname) in a calling function may be initialized in a +called function to reference a local parameter var(rname). In this +case, when the called function returns, var(pname) is no longer +associated with that local. However, the initializer var(rname) +continues to be substituted when `tt($)var(pname)' is used, and +therefore may become a reference to another parameter in the calling +function. It is recommended that placeholders be initialized soon +after they are declared, to make it clear what they reference. + texinode(Parameters Set By The Shell)(Parameters Used By The Shell)(Named References)(Parameters) sect(Parameters Set By The Shell) In the parameter lists that follow, the mark `<S>' indicates that the diff --git a/Src/params.c b/Src/params.c index e6fb7fcd0..b4179a4f4 100644 --- a/Src/params.c +++ b/Src/params.c @@ -5828,7 +5828,9 @@ static void scanendscope(HashNode hn, UNUSED(int flags)) { Param pm = (Param)hn; + Param hidden = NULL; if (pm->level > locallevel) { + hidden = pm->old; if ((pm->node.flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL) { /* * Removable specials are normal in that they can be removed @@ -5891,9 +5893,15 @@ scanendscope(HashNode hn, UNUSED(int flags)) export_param(pm); } else unsetparam_pm(pm, 0, 0); - } else if ((pm->node.flags & PM_NAMEREF) && - pm->base > pm->level && pm->base > locallevel) + } + if (hidden) + pm = hidden; + if (pm && (pm->node.flags & PM_NAMEREF) && + pm->base >= pm->level && pm->base >= locallevel) { pm->base = locallevel; + if (pm->level < locallevel && (pm->node.flags & PM_UPPER)) + pm->node.flags &= ~PM_UPPER; + } } @@ -6404,14 +6412,14 @@ setscope(Param pm) } else if (!pm->base) { pm->base = basepm->level; if ((pm->node.flags & PM_UPPER) && - (basepm = upscope(basepm, -(locallevel-1)))) + (basepm = upscope(basepm, -locallevel))) pm->base = basepm->level; } } else if (pm->base < locallevel && refname && (basepm = (Param)getparamnode(realparamtab, refname))) { pm->base = basepm->level; if ((pm->node.flags & PM_UPPER) && - (basepm = upscope(basepm, -(locallevel-1)))) + (basepm = upscope(basepm, -locallevel))) pm->base = basepm->level; } if (pm->base > pm->level) {
Attachment:
K01nameref.ztst
Description: Binary data