1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-09-26 18:01:03 +02:00

53602: "typeset -nu" always refers to at a call level above the declaration

This commit is contained in:
Bart Schaefer 2025-05-12 21:29:16 -07:00
parent 1706805d46
commit b0fa403a3d
4 changed files with 39 additions and 18 deletions

View file

@ -1,3 +1,9 @@
2025-05-12 Bart Schaefer <schaefer@zsh.org>
* 53602: Doc/Zsh/func.yo, Src/params.c, Test/K01nameref.ztst:
Update "typeset -nu" behavior to always refer to parameters
at a call level above the declaration of the named reference.
2025-05-09 Bart Schaefer <schaefer@zsh.org> 2025-05-09 Bart Schaefer <schaefer@zsh.org>
* unposted: Src/params.c: fix bad pointer found by valgrind * unposted: Src/params.c: fix bad pointer found by valgrind

View file

@ -23,9 +23,11 @@ declared in an earlier function scope.
(See noderef(Local Parameters).) (See noderef(Local Parameters).)
A named parameter declared with the `tt(-n)' option to any of the 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 `tt(typeset)' acts as a reference to another parameter, which may
the time of assignment to the named reference, which may be at a be at a different call level than the declaring function. When the
different call level than the declaring function. For this reason, `tt(-u)' option is also given, the referenced parameter is always
found at a call level above the function where the reference is
declared, otherwise the reference scope is dynamic. For this reason,
it is good practice to declare a named reference as soon as the 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 referent parameter is in scope, and as early as possible in the
function if the reference is to a parameter in a calling scope. function if the reference is to a parameter in a calling scope.

View file

@ -5899,9 +5899,8 @@ scanendscope(HashNode hn, UNUSED(int flags))
pm = hidden; pm = hidden;
if (pm && (pm->node.flags & PM_NAMEREF) && if (pm && (pm->node.flags & PM_NAMEREF) &&
pm->base >= pm->level && pm->base >= locallevel) { pm->base >= pm->level && pm->base >= locallevel) {
/* Should never get here for a -u reference */
pm->base = locallevel; pm->base = locallevel;
if (pm->level < locallevel && (pm->node.flags & PM_UPPER))
pm->node.flags &= ~PM_UPPER;
} }
} }
@ -6307,7 +6306,9 @@ resolve_nameref(Param pm, const Asgment stop)
if (pm) { if (pm) {
if (!(stop && (stop->flags & (PM_LOCAL)))) { if (!(stop && (stop->flags & (PM_LOCAL)))) {
int scope = ((pm->node.flags & PM_NAMEREF) ? int scope = ((pm->node.flags & PM_NAMEREF) ?
((pm->node.flags & PM_UPPER) ? -(pm->base) : ((pm->node.flags & PM_UPPER) ?
/* pm->base == 0 means not set yet */
-(pm->base ? pm->base : pm->level) :
pm->base) : ((Param)hn)->level); pm->base) : ((Param)hn)->level);
hn = (HashNode)upscope((Param)hn, scope); hn = (HashNode)upscope((Param)hn, scope);
} }
@ -6413,14 +6414,14 @@ setscope(Param pm)
} else if (!pm->base) { } else if (!pm->base) {
pm->base = basepm->level; pm->base = basepm->level;
if ((pm->node.flags & PM_UPPER) && if ((pm->node.flags & PM_UPPER) &&
(basepm = upscope(basepm, -locallevel))) (basepm = upscope(basepm, -(pm->level))))
pm->base = basepm->level; pm->base = basepm->level;
} }
} else if (pm->base < locallevel && refname && } else if (pm->base < locallevel && refname &&
(basepm = (Param)getparamnode(realparamtab, refname))) { (basepm = (Param)getparamnode(realparamtab, refname))) {
pm->base = basepm->level; pm->base = basepm->level;
if ((pm->node.flags & PM_UPPER) && if ((pm->node.flags & PM_UPPER) &&
(basepm = upscope(basepm, -locallevel))) (basepm = upscope(basepm, -(pm->level))))
pm->base = basepm->level; pm->base = basepm->level;
} }
if (pm->base > pm->level) { if (pm->base > pm->level) {

View file

@ -767,6 +767,18 @@ F:typeset cannot bypass a name in the local scope, even via nameref
>typeset -a foo=( alpha beta gamma ) >typeset -a foo=( alpha beta gamma )
>typeset -g foo=3 >typeset -g foo=3
() {
# scope with no parameters
() {
local -nu upref=$1
local var=at_upref
print -- $upref
} var
}
0:up-reference part 15, non-existent parameter in outer scope
# no output expected
>
if [[ $options[typesettounset] != on ]]; then if [[ $options[typesettounset] != on ]]; then
ZTST_skip='Ignoring zmodload bug that resets TYPESET_TO_UNSET' ZTST_skip='Ignoring zmodload bug that resets TYPESET_TO_UNSET'
setopt typesettounset setopt typesettounset
@ -1088,16 +1100,16 @@ F:previously this could create an infinite recursion and crash
>h:1: rs= - ra= - rs1= - ra1= >h:1: rs= - ra= - rs1= - ra1=
>h:2: rs= - ra= - rs1= - ra1= >h:2: rs= - ra= - rs1= - ra1=
>i:1: rs= - ra= - rs1= - ra1= >i:1: rs= - ra= - rs1= - ra1=
>i:2: rs=h - ra=h - rs1=h - ra1=h >i:2: rs=g - ra=g - rs1=g - ra1=g
>j:1: rs=h - ra=h - rs1=h - ra1=h >j:1: rs=g - ra=g - rs1=g - ra1=g
>j:2: rs=h - ra=h - rs1=h - ra1=h >j:2: rs=g - ra=g - rs1=g - ra1=g
>i:3: rs=h - ra=h - rs1=h - ra1=h >i:3: rs=g - ra=g - rs1=g - ra1=g
>k:1: rs=h - ra=h - rs1=h - ra1=h >k:1: rs=g - ra=g - rs1=g - ra1=g
>k:2: rs=h - ra=h - rs1=h - ra1=h >k:2: rs=g - ra=g - rs1=g - ra1=g
>h:3: rs=h - ra=h - rs1=h - ra1=h >h:3: rs=g - ra=g - rs1=g - ra1=g
>k:1: rs=h - ra=h - rs1=h - ra1=h >k:1: rs=g - ra=g - rs1=g - ra1=g
>k:2: rs=h - ra=h - rs1=h - ra1=h >k:2: rs=g - ra=g - rs1=g - ra1=g
>g:3: rs=g - ra=g - rs1=g - ra1=g >g:3: rs=f - ra=f - rs1=f - ra1=f
e '' 6 e '' 6
0:assignment at different scope than declaration, '' 6 0:assignment at different scope than declaration, '' 6