summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBart Schaefer <schaefer@zsh.org>2025-05-06 10:46:23 -0700
committerBart Schaefer <schaefer@zsh.org>2025-05-06 10:50:53 -0700
commitf555e902db1e4acec7961374153ec19843e2833a (patch)
tree1f0cc5ac9e804601cf41f19abfbc2921b42b39c4
parent53535: fix builds with interactive manpagers (diff)
downloadzsh-f555e902db1e4acec7961374153ec19843e2833a.tar
zsh-f555e902db1e4acec7961374153ec19843e2833a.tar.gz
zsh-f555e902db1e4acec7961374153ec19843e2833a.tar.bz2
zsh-f555e902db1e4acec7961374153ec19843e2833a.tar.lz
zsh-f555e902db1e4acec7961374153ec19843e2833a.tar.xz
zsh-f555e902db1e4acec7961374153ec19843e2833a.tar.zst
zsh-f555e902db1e4acec7961374153ec19843e2833a.zip
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
-rw-r--r--ChangeLog9
-rw-r--r--Src/params.c28
-rw-r--r--Test/K01nameref.ztst160
3 files changed, 191 insertions, 6 deletions
diff --git a/ChangeLog b/ChangeLog
index bff0b5146..5ee7ad85e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/Src/params.c b/Src/params.c
index c10236a0d..e6fb7fcd0 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -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;
diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst
index 1603ab1b9..cc689613b 100644
--- a/Test/K01nameref.ztst
+++ b/Test/K01nameref.ztst
@@ -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.