diff options
| author | Bart Schaefer <schaefer@zsh.org> | 2025-05-06 10:46:23 -0700 |
|---|---|---|
| committer | Bart Schaefer <schaefer@zsh.org> | 2025-05-06 10:50:53 -0700 |
| commit | f555e902db1e4acec7961374153ec19843e2833a (patch) | |
| tree | 1f0cc5ac9e804601cf41f19abfbc2921b42b39c4 | |
| parent | 53535: fix builds with interactive manpagers (diff) | |
| download | zsh-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-- | ChangeLog | 9 | ||||
| -rw-r--r-- | Src/params.c | 28 | ||||
| -rw-r--r-- | Test/K01nameref.ztst | 160 |
3 files changed, 191 insertions, 6 deletions
@@ -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. |
