1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-06-17 21:18:06 +02:00

53546,53557 (plus test): Fix scoping of "placeholder" named references

When using placeholders declared several levels earlier than assignment:
1) Searching "up" for "typeset -n -u" could find deeper locals than intended
2) Searching for a subscript reference could skip to the top level
3) Exiting a function scope could incorrectly change the reference level
This commit is contained in:
Bart Schaefer 2025-05-06 10:46:23 -07:00
parent d89f3a1d08
commit f555e902db
3 changed files with 191 additions and 6 deletions

View file

@ -1,3 +1,12 @@
2025-05-05 Bart Schaefer <schaefer@zsh.org>
* 53557 (plus test): Src/params.c, Test/K01nameref.ztst:
Fix one more scoping issue with "typeset -u -n" references
* 53546 (plus test): Src/params.c, Test/K01nameref.ztst:
Fix several errors when using "placeholder" named references
declared at scopes surrounding the scope where they are assigned.
2025-05-04 dana@dana.is <dana@dana.is>
* Max Coplan: 53535: Util/helpfiles: fix builds with

View file

@ -2231,7 +2231,13 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
*ss = 0;
}
Param p1 = (Param)gethashnode2(paramtab, ref);
if (!(p1 && (pm = upscope(p1, pm->base))) ||
if (p1) {
int scope = ((pm->node.flags & PM_NAMEREF) ?
((pm->node.flags & PM_UPPER) ? -(pm->base) :
pm->base) : locallevel);
pm = upscope(p1, scope);
}
if (!(p1 && pm) ||
((pm->node.flags & PM_UNSET) &&
!(pm->node.flags & PM_DECLARED)))
return NULL;
@ -5885,7 +5891,8 @@ 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)
} else if ((pm->node.flags & PM_NAMEREF) &&
pm->base > pm->level && pm->base > locallevel)
pm->base = locallevel;
}
@ -6291,7 +6298,7 @@ resolve_nameref(Param pm, const Asgment stop)
if (pm) {
if (!(stop && (stop->flags & (PM_LOCAL)))) {
int scope = ((pm->node.flags & PM_NAMEREF) ?
((pm->node.flags & PM_UPPER) ? -1 :
((pm->node.flags & PM_UPPER) ? -(pm->base) :
pm->base) : ((Param)hn)->level);
hn = (HashNode)upscope((Param)hn, scope);
}
@ -6365,6 +6372,7 @@ setscope(Param pm)
if (t) {
pm->width = t - refname;
*t = '[';
refname = dupstrpfx(refname, pm->width);
}
if (basepm) {
if (basepm->node.flags & PM_NAMEREF) {
@ -6393,11 +6401,19 @@ setscope(Param pm)
break;
}
}
} else
} else if (!pm->base) {
pm->base = basepm->level;
if ((pm->node.flags & PM_UPPER) &&
(basepm = upscope(basepm, -(locallevel-1))))
pm->base = basepm->level;
}
} else if (pm->base < locallevel && refname &&
(basepm = (Param)getparamnode(realparamtab, refname)))
(basepm = (Param)getparamnode(realparamtab, refname))) {
pm->base = basepm->level;
if ((pm->node.flags & PM_UPPER) &&
(basepm = upscope(basepm, -(locallevel-1))))
pm->base = basepm->level;
}
if (pm->base > pm->level) {
if (EMULATION(EMULATE_KSH)) {
zerr("%s: global reference cannot refer to local variable",
@ -6422,7 +6438,7 @@ upscope(Param pm, int reflevel)
{
Param up = pm->old;
while (up && up->level >= reflevel) {
if (reflevel < 0 && up->level < locallevel)
if (reflevel < 0 && up->level < -(reflevel))
break;
pm = up;
up = up->old;

View file

@ -7,6 +7,76 @@
: ${ZTST_continue::=1}
# The following test allows to assess what different types of named
# references refer to during their lifetime depending on where they
# were initialized and whether they were defined with or without the
# "-u" flag.
#
# The first parameter determines whether the named references are
# defined with or without the flag "-u".
#
# The second parameter determines where the named references are
# initialized. In all cases the named references are defined at the
# start of function "g". With value "0" they are initialized at the
# same place (in the same statement). With the other values, the
# initialization is delayed until later, the greater the value and the
# later the initialization.
function e() {
local s=$0 a=($0);
f "$@";
}
function f() {
local s=$0 a=($0);
g "$@";
}
function g() {
if (($2)); then local -n $1 rs ra rs1 ra1;
else local -n $1 rs=s ra=a rs1="s[1]" ra1="a[1]"; fi;
if (($2 == 1)); then rs=s; ra=a; rs1="s[1]"; ra1="a[1]"; fi;
echo "$0:1: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
local s=$0 a=($0);
if (($2 == 2)); then rs=s; ra=a; rs1="s[1]"; ra1="a[1]"; fi;
echo "$0:2: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
h "$@";
echo "$0:3: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
}
function h() {
if (($2 == 3)); then rs=s; ra=a; rs1="s[1]"; ra1="a[1]"; fi;
echo "$0:1: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
local s=$0 a=($0);
if (($2 == 4)); then rs=s; ra=a; rs1="s[1]"; ra1="a[1]"; fi;
echo "$0:2: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
i "$@";
echo "$0:3: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
# Check that re-entering the same scope doesn't undo scope exit
k "$@"
}
function i() {
if (($2 == 5)); then rs=s; ra=a; rs1="s[1]"; ra1="a[1]"; fi;
echo "$0:1: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
local s=$0 a=($0);
if (($2 == 6)); then rs=s; ra=a; rs1="s[1]"; ra1="a[1]"; fi;
echo "$0:2: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
j "$@";
echo "$0:3: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
k "$@";
}
function j() {
echo "$0:1: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
local s=$0 a=($0);
echo "$0:2: rs=$rs - ra=$ra - rs1=$rs1 - ra1=$ra1";
}
functions -c j k
local s=T a=(T);
%test
typeset -n ptr
@ -902,6 +972,96 @@ F:previously this could create an infinite recursion and crash
1:create nameref by pattern match not allowed
*?*typeset:1: -m not allowed with -n
e -u 0
0:assignment at different scope than declaration, -u 0
>g:1: rs=f - ra=f - rs1=f - ra1=f
>g:2: rs=f - ra=f - rs1=f - ra1=f
>h:1: rs=f - ra=f - rs1=f - ra1=f
>h:2: rs=f - ra=f - rs1=f - ra1=f
>i:1: rs=f - ra=f - rs1=f - ra1=f
>i:2: rs=f - ra=f - rs1=f - ra1=f
>j:1: rs=f - ra=f - rs1=f - ra1=f
>j:2: rs=f - ra=f - rs1=f - ra1=f
>i:3: rs=f - ra=f - rs1=f - ra1=f
>k:1: rs=f - ra=f - rs1=f - ra1=f
>k:2: rs=f - ra=f - rs1=f - ra1=f
>h:3: rs=f - ra=f - rs1=f - ra1=f
>k:1: rs=f - ra=f - rs1=f - ra1=f
>k:2: rs=f - ra=f - rs1=f - ra1=f
>g:3: rs=f - ra=f - rs1=f - ra1=f
e '' 0
0:assignment at different scope than declaration, '' 0
>g:1: rs=f - ra=f - rs1=f - ra1=f
>g:2: rs=f - ra=f - rs1=f - ra1=f
>h:1: rs=f - ra=f - rs1=f - ra1=f
>h:2: rs=f - ra=f - rs1=f - ra1=f
>i:1: rs=f - ra=f - rs1=f - ra1=f
>i:2: rs=f - ra=f - rs1=f - ra1=f
>j:1: rs=f - ra=f - rs1=f - ra1=f
>j:2: rs=f - ra=f - rs1=f - ra1=f
>i:3: rs=f - ra=f - rs1=f - ra1=f
>k:1: rs=f - ra=f - rs1=f - ra1=f
>k:2: rs=f - ra=f - rs1=f - ra1=f
>h:3: rs=f - ra=f - rs1=f - ra1=f
>k:1: rs=f - ra=f - rs1=f - ra1=f
>k:2: rs=f - ra=f - rs1=f - ra1=f
>g:3: rs=f - ra=f - rs1=f - ra1=f
e -u 2
0:assignment at different scope than declaration, -u 2
>g:1: rs= - ra= - rs1= - ra1=
>g:2: rs=f - ra=f - rs1=f - ra1=f
>h:1: rs=f - ra=f - rs1=f - ra1=f
>h:2: rs=f - ra=f - rs1=f - ra1=f
>i:1: rs=f - ra=f - rs1=f - ra1=f
>i:2: rs=f - ra=f - rs1=f - ra1=f
>j:1: rs=f - ra=f - rs1=f - ra1=f
>j:2: rs=f - ra=f - rs1=f - ra1=f
>i:3: rs=f - ra=f - rs1=f - ra1=f
>k:1: rs=f - ra=f - rs1=f - ra1=f
>k:2: rs=f - ra=f - rs1=f - ra1=f
>h:3: rs=f - ra=f - rs1=f - ra1=f
>k:1: rs=f - ra=f - rs1=f - ra1=f
>k:2: rs=f - ra=f - rs1=f - ra1=f
>g:3: rs=f - ra=f - rs1=f - ra1=f
e -u 6
0:assignment at different scope than declaration, -u 6
>g:1: rs= - ra= - rs1= - ra1=
>g:2: rs= - ra= - rs1= - ra1=
>h:1: rs= - ra= - rs1= - ra1=
>h:2: rs= - ra= - rs1= - ra1=
>i:1: rs= - ra= - rs1= - ra1=
>i:2: rs=h - ra=h - rs1=h - ra1=h
>j:1: rs=h - ra=h - rs1=h - ra1=h
>j:2: rs=h - ra=h - rs1=h - ra1=h
>i:3: rs=h - ra=h - rs1=h - ra1=h
>k:1: rs=h - ra=h - rs1=h - ra1=h
>k:2: 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:2: rs=h - ra=h - rs1=h - ra1=h
>g:3: rs=f - ra=f - rs1=f - ra1=f
e '' 6
0:assignment at different scope than declaration, '' 6
>g:1: rs= - ra= - rs1= - ra1=
>g:2: rs= - ra= - rs1= - ra1=
>h:1: rs= - ra= - rs1= - ra1=
>h:2: rs= - ra= - rs1= - ra1=
>i:1: rs= - ra= - rs1= - ra1=
>i:2: rs=i - ra=i - rs1=i - ra1=i
>j:1: rs=i - ra=i - rs1=i - ra1=i
>j:2: rs=i - ra=i - rs1=i - ra1=i
>i:3: rs=i - ra=i - rs1=i - ra1=i
>k:1: rs=i - ra=i - rs1=i - ra1=i
>k:2: rs=i - ra=i - rs1=i - ra1=i
>h:3: rs=h - ra=h - rs1=h - ra1=h
>k:1: rs=h - ra=h - rs1=h - ra1=h
>k:2: rs=h - ra=h - rs1=h - ra1=h
>g:3: rs=g - ra=g - rs1=g - ra1=g
#
# The following tests are run in interactive mode, using PS1 as an
# assignable special with side-effects. This crashed at one time.