summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--Doc/Zsh/func.yo8
-rw-r--r--Src/params.c11
-rw-r--r--Test/K01nameref.ztst32
4 files changed, 39 insertions, 18 deletions
diff --git a/ChangeLog b/ChangeLog
index f8234461c..4ec70d964 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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>
* unposted: Src/params.c: fix bad pointer found by valgrind
diff --git a/Doc/Zsh/func.yo b/Doc/Zsh/func.yo
index 7b71e34e9..9558b11c4 100644
--- a/Doc/Zsh/func.yo
+++ b/Doc/Zsh/func.yo
@@ -23,9 +23,11 @@ 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,
+`tt(typeset)' acts as a reference to another parameter, which may
+be at a different call level than the declaring function. When the
+`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
referent parameter is in scope, and as early as possible in the
function if the reference is to a parameter in a calling scope.
diff --git a/Src/params.c b/Src/params.c
index 1a2bf62d2..fec1b2c02 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -5899,9 +5899,8 @@ scanendscope(HashNode hn, UNUSED(int flags))
pm = hidden;
if (pm && (pm->node.flags & PM_NAMEREF) &&
pm->base >= pm->level && pm->base >= locallevel) {
+ /* Should never get here for a -u reference */
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 (!(stop && (stop->flags & (PM_LOCAL)))) {
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);
hn = (HashNode)upscope((Param)hn, scope);
}
@@ -6413,14 +6414,14 @@ setscope(Param pm)
} else if (!pm->base) {
pm->base = basepm->level;
if ((pm->node.flags & PM_UPPER) &&
- (basepm = upscope(basepm, -locallevel)))
+ (basepm = upscope(basepm, -(pm->level))))
pm->base = basepm->level;
}
} else if (pm->base < locallevel && refname &&
(basepm = (Param)getparamnode(realparamtab, refname))) {
pm->base = basepm->level;
if ((pm->node.flags & PM_UPPER) &&
- (basepm = upscope(basepm, -locallevel)))
+ (basepm = upscope(basepm, -(pm->level))))
pm->base = basepm->level;
}
if (pm->base > pm->level) {
diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst
index 30b6673e0..54f0aaf68 100644
--- a/Test/K01nameref.ztst
+++ b/Test/K01nameref.ztst
@@ -767,6 +767,18 @@ F:typeset cannot bypass a name in the local scope, even via nameref
>typeset -a foo=( alpha beta gamma )
>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
ZTST_skip='Ignoring zmodload bug that resets TYPESET_TO_UNSET'
setopt typesettounset
@@ -1088,16 +1100,16 @@ F:previously this could create an infinite recursion and crash
>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=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
+>i:2: rs=g - ra=g - rs1=g - ra1=g
+>j:1: rs=g - ra=g - rs1=g - ra1=g
+>j:2: rs=g - ra=g - rs1=g - ra1=g
+>i:3: rs=g - ra=g - rs1=g - ra1=g
+>k:1: rs=g - ra=g - rs1=g - ra1=g
+>k:2: rs=g - ra=g - rs1=g - ra1=g
+>h:3: rs=g - ra=g - rs1=g - ra1=g
+>k:1: rs=g - ra=g - rs1=g - ra1=g
+>k:2: rs=g - ra=g - rs1=g - ra1=g
+>g:3: rs=f - ra=f - rs1=f - ra1=f
e '' 6
0:assignment at different scope than declaration, '' 6