diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 97a82226b..92917c06c 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -2041,12 +2041,13 @@ cindex(named reference) cindex(reference, named) The flag tt(-n) creates a em(named reference) to another parameter. The second parameter need not exist at the time the reference is -created. No attributes except tt(-g) may be used in conjunction with +created. Only tt(-g) and tt(-r) may be used in conjunction with tt(-n). The var(name) so created may not be an array element nor use a subscript, but the var(value) assigned may be any valid parameter name syntax, even a subscripted array element (including an associative array element) or an array slice, which is evaluated when the named -reference is expanded. +reference is expanded. It is an error for a named reference to refer +to itself, even indirectly through a chain of references. See ifzman(zmanref(zshexpn))ifnzman(noderef(Parameter Expansion)) and ifzman(zmanref(zshparam))ifnzman(noderef(Parameters)) for details of the behavior of named references. @@ -2443,7 +2444,7 @@ with the command `tt(zmodload -F zsh/rlimits b:unlimit)'. ) findex(unset) cindex(parameters, unsetting) -item(tt(unset) [ tt(-fmv) ] var(name) ...)( +item(tt(unset) [ tt(-fmv) ] [ tt(-n) ] var(name) ...)( Each named parameter is unset. Local parameters remain local even if unset; they appear unset within scope, but the previous value will still reappear when the scope ends. @@ -2457,10 +2458,13 @@ be quoted) and all parameters with matching names are unset. Note that this cannot be used when unsetting associative array elements, as the subscript will be treated as part of the pattern. -The tt(-v) flag specifies that var(name) refers to parameters. This is the -default behaviour. +The tt(-v) flag specifies that var(name) refers to parameters. This is +the default behaviour. If the tt(-n) option is supplied, and +var(name) is a a named reference, var(name) will be unset rather than +the variable it references. -tt(unset -f) is equivalent to tt(unfunction). +tt(unset -f) is equivalent to tt(unfunction). The tt(-n) option has +no effect with tt(-f). ) findex(unsetopt) cindex(options, unsetting) diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index 8b1c69c55..ef01794e6 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -1581,7 +1581,10 @@ is interpreted at the time tt(${)var(pname)tt(}) is expanded. Any form of subscript is allowed, including those that select individual elements, substrings of scalar strings, or multiple elements as with array slices or the `tt((i))', `tt((I))', `tt((r))', `tt((R))' and -`tt((w))' subscript flags. +`tt((w))' subscript flags. However, the subscript is evaluated with +the tt(NO_EXEC) option in effect, so command substitution and other +similar constructs produce no output, although are not syntactically +excluded. When var(rname) is an array (but not an array element or slice), the named reference may also be used in substitutions requiring an diff --git a/Doc/Zsh/func.yo b/Doc/Zsh/func.yo index 12db3f56a..d4914df7a 100644 --- a/Doc/Zsh/func.yo +++ b/Doc/Zsh/func.yo @@ -13,6 +13,40 @@ Functions are executed like commands with the arguments passed as positional parameters. (See noderef(Command Execution).) +Parameters declared by any of the `tt(typeset)' family of commands +during the execution of a function become em(local) to the function +unless the `tt(-g)' option is used. This is the em(scope) of the +parameter, which extends dynamically to any other functions called by +the declaring function. In most cases, local parameters take the +place of any other parameter having the same name that was assigned or +declared in an earlier function scope. +(See noderef(Local Parameters).) + +A named parameter declared with the `tt(-n)' option to any of the +`tt(typeset)' commands becomes a reference to a parameter in scope at +the time of assignment to the named reference, which may be at a +different call level than the declaring function. For this reason, +it is good practice to declare a named reference as soon as the +referent parameter is in scope, and as early as possible in the +function if the reference is to a parameter in a calling scope. + +A typical use of named references is to pass the name +of the referent as a positional parameter. For example, +ifzman() +example(pop+LPAR()RPAR() { + local -n ref=$1 + local last=$ref[$#ref] + ref[$#ref]=LPAR()RPAR() + print -r -- $last +} +array=LPAR() a list of five values RPAR() +pop array) + +prints the word `tt(values)' and shortens `tt($array)' to +`tt(LPAR() a list of five RPAR())'. There are no local parameters in +tt(pop) at the time `tt(ref=$1)' is assigned, so `tt(ref)' becomes a +reference to `tt(array)' in the caller. + Functions execute in the same process as the caller and share all files and present working directory with the diff --git a/Doc/Zsh/grammar.yo b/Doc/Zsh/grammar.yo index 9af211090..1b834f41a 100644 --- a/Doc/Zsh/grammar.yo +++ b/Doc/Zsh/grammar.yo @@ -187,6 +187,9 @@ Expand the list of var(word)s, and set the parameter var(name) to each of them in turn, executing var(list) each time. If the `tt(in) var(word)' is omitted, use the positional parameters instead of the var(word)s. +If any var(name) has been declared as a named reference, +the corresponding var(word) is treated as the name of a +parameter and var(name) is made a reference to that. The var(term) consists of one or more newline or tt(;) which terminate the var(word)s, and are optional when the diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index c7ecf0f64..946c00793 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -656,8 +656,8 @@ When a em(named reference) is created with `tt(typeset -n)', all uses of var(pname) in assignments and expansions instead assign to or expand var(rname). This also applies to `tt(unset )var(pname)' and to most subsequent uses of `tt(typeset)' with the exception of -`tt(typeset -n)' and `tt(typeset +n)', so to remove a named reference -it is necessary to use one of: +`tt(typeset -n)' and `tt(typeset +n)', so to remove a named reference, +use either `tt(unset -n )var(pname)' or one of: ifzman() example(tt(typeset -n )var(pname) tt(typeset +n )var(pname)) diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst index a663194a7..61c2b006a 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -23,6 +23,12 @@ ptr=var typeset -n 0:assign nameref placeholder +>ptr=var + + typeset ptr=var + typeset -n ptr + typeset -n +0:convert scalar to nameref >ptr=var typeset -n ptr=var @@ -46,6 +52,11 @@ F:Other type changes are fatal errors, should this also be? >typeset -n ptr=var >typeset -t var + typeset -n ptr=var[2] + typeset -t ptr +1:change type of referenced array element +*?*var\[2\]: can't change type via subscript reference + typeset -n ptr[1]=var 1:illegal nameref name *?*reference variable cannot be an array @@ -91,6 +102,14 @@ F:Other type changes are fatal errors, should this also be? typeset -p var 0:unset via nameref + typeset -n ptr=var + typeset var=value + unset -n ptr + typeset -p var ptr +0:unset of the nameref itself +F:If earlier tests change, might get "no such variable" here +>typeset var=value + typeset -n ptr=var typeset var=value typeset -p ptr var @@ -105,11 +124,11 @@ F:Other type changes are fatal errors, should this also be? typeset -n ptr=var ptr=value typeset -p var ptr - unset var # for next test 0:assign new scalar via nameref >typeset -g var=value >typeset -n ptr=var + unset var typeset -n ptr=var typeset var=(val1 val2) typeset -p ptr var @@ -132,11 +151,11 @@ F:unexpected side-effects of previous tests typeset -n ptr=var ptr=(val1 val2) typeset -p var ptr - unset var # for next test 0:assign new array via nameref >typeset -g -a var=( val1 val2 ) >typeset -n ptr=var + unset var typeset -n ptr2=var typeset -n ptr1=ptr2 typeset var=value @@ -178,12 +197,12 @@ F:unexpected side-effects of previous tests typeset -n ptr1=ptr2 typeset ptr1=newvalue typeset -p ptr1 ptr2 var - unset var # for next test 0:typeset new parameter indirectly >typeset -n ptr1=ptr2 >typeset -n ptr2=var >typeset var=newvalue + unset var typeset -n ptr2=var typeset -n ptr1=ptr2 typeset var=value @@ -219,19 +238,18 @@ F:unexpected side-effects of previous tests typeset -n ptr1=ptr2 typeset ptr1=(val1 val2) typeset -p ptr1 ptr2 var - unset var # for next test 0:typeset new array indirectly >typeset -n ptr1=ptr2 >typeset -n ptr2=var >typeset -a var=( val1 val2 ) - typeset -p ptr1 ptr2 var + typeset -p ptr1 ptr2 1:check state of paramtab FOUR F:unexpected side-effects of previous tests *?*no such variable: ptr1 *?*no such variable: ptr2 -*?*no such variable: var + unset var typeset -n ptr2=var typeset -n ptr1=ptr2 ptr1=(val1 val2) @@ -244,6 +262,20 @@ F:unexpected side-effects of previous tests typeset -n ptr1=ptr2 typeset -n ptr2=ptr1 1:direct nameref loop not allowed +*?*invalid self reference + + unset var + typeset -gn ptr1=var + typeset -p ptr1 +0:global reference to unset var +>typeset -g -n ptr1=var + + unset -n ptr1 + typeset -gn ptr1 + typeset -p ptr1 + ptr1=ptr1 +1:global direct reference +>typeset -g -n ptr1 *?*invalid self reference typeset -n ptr1=ptr2 @@ -252,28 +284,39 @@ F:unexpected side-effects of previous tests 1:indirect nameref loop not allowed *?*invalid self reference + typeset -n ptr1 ptr2 + ptr1=ptr2 + ptr2=ptr1 +1:looping assignment not allowed +*?*invalid self reference + + unset -n ptr2 typeset -n ptr2='path[2]' print -r -- $ptr2 0q:nameref to array element, no braces >${path[2]} + unset -n ptr2 typeset -n ptr2='path[2]' print -r -- ${ptr2} 0q:nameref to array element, with braces >${path[2]} + unset -n ptr1 typeset -A hash=(x MISS y HIT) typeset -n ptr1='hash[y]' print -r -- $ptr1 0:nameref to hash element, no braces >HIT + unset -n ptr1 typeset -A hash=(x MISS y HIT) typeset -n ptr1='hash[y]' print -r -- ${ptr1} 0:nameref to hash element, with braces >HIT + unset -n ptr2 typeset -a ary=(1 2) typeset -n ptr2='ary[2]' ptr2=TWO @@ -281,6 +324,7 @@ F:unexpected side-effects of previous tests 0:assign array element by nameref >typeset -a ary=( 1 TWO ) + unset -n ptr2 typeset -n ptr2='ary[2]' ptr2=TWO typeset -p ary @@ -288,6 +332,7 @@ F:unexpected side-effects of previous tests F:ksh93 does not implement this either >typeset -a ary=( '' TWO ) + unset -n ptr1 typeset -A hash=(x MISS y MISS) typeset -n ptr1='hash[y]' ptr1=HIT @@ -295,6 +340,7 @@ F:ksh93 does not implement this either 0:assign to hash element by nameref >typeset -A hash=( [x]=MISS [y]=HIT ) + unset -n ptr1 typeset -A hash typeset -n ptr1='hash[y]' ptr1=HIT @@ -303,10 +349,13 @@ F:ksh93 does not implement this either F:ksh93 does not implement this either >typeset -A hash=( [y]=HIT ) + unset -n ptr1 typeset -n ptr1='not good' 1:invalid nameref *?*invalid variable name: not good + unset -n ptr1 + unset hash typeset -A hash typeset -n ptr1='hash[y]' print ${ptr1::=HIT} @@ -316,24 +365,25 @@ F:ksh93 does not implement this either >typeset -n ptr1='hash[y]' >typeset -A hash=( [y]=HIT ) + unset -n ptr + unset gval typeset -n ptr=gval gval=global () { local gval=local; print $ptr; typeset -p ptr gval } - unset gval # for next test 0:up-reference part 1 >global >typeset -g -n ptr=gval >typeset gval=local - typeset -p ptr ptr1 ptr2 val gval + typeset -p ptr ptr1 ptr2 val 1:check state of paramtab FIVE F:unexpected side-effects of previous tests *?*no such variable: ptr *?*no such variable: ptr1 *?*no such variable: ptr2 *?*no such variable: val -*?*no such variable: gval + unset gval typeset -n ptr1=gval typeset gval () { typeset gval=local; ptr1=global } @@ -509,4 +559,106 @@ F:Same test, should part 5 output look like this? >bry[2] >ipsum + unset -n ref + unset var + typeset -n ref=var + typeset var=GLOBAL + () { + typeset -n ref=$1 + print -r $ref + ref=RESET + typeset -p ref var + } ref + typeset -p ref var +0:local reference points to same-name global reference, part 1 +>GLOBAL +>typeset -n ref=ref +>typeset -g var=RESET +>typeset -n ref=var +>typeset var=RESET + + unset -n ref + unset var + typeset -n ref=var + () { + typeset -n ref=$1 + print -r $ref + ref=RESET + typeset -p ref var + } ref + typeset -p ref var +0:local reference points to same-name global reference, part 2 +> +>typeset -n ref=ref +>typeset -g var=RESET +>typeset -n ref=var +>typeset -g var=RESET + + unset -n ref + unset one + typeset -n ref + typeset one=ONE + for ref in one ref two; do print -r $ref; done +1:for-loop variable is a reference, part 1 +>ONE +*?*ref: invalid self reference + + unset -n ref + unset one + typeset -n ref + () { + typeset one=ONE + for ref in one ref two; do print -r ${(t)ref}; done + } +1:for-loop variable is a reference, part 2 +>scalar-local +*?*ref: invalid self reference + + unset -n ref + unset one var + typeset -n ref=var + () { + typeset one=ONE + typeset -n ref=ref + for ref in one ref two; do + typeset -p ref + print -r $ref + done + typeset -p ref + } + typeset -p ref +0:for-loop variable is a reference, part 3 +>typeset -n ref=one +>ONE +>typeset -n ref=ref +> +>typeset -n ref=two +> +>typeset -n ref=two +>typeset -n ref=var + + unset -n ref + unset one + typeset -n ref + () { + setopt localoptions warn_nested_var + typeset one=ONE + for ref in one two; do print -r ${(t)ref}; done + typeset -n ref + for ref in one two; do print -r ${(t)ref}; done + } +0:for-loop variable is a reference, part 4, warnings +>scalar-local +> +>scalar-local +> +*?*ref: global reference to local variable: one + + typeset -n ptr='ary[$(echo 2)]' + typeset -a ary=(one two three) + print $ptr +1:attempt deferred command substitution in subscript +F:runs in `setopt noexec` so $(...) returns nothing +*?*bad math expression: empty string + %clean