Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
PATCH: typeset -nu always relative to declaration
- X-seq: zsh-workers 53602
- From: Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx>
- To: Zsh hackers list <zsh-workers@xxxxxxx>
- Subject: PATCH: typeset -nu always relative to declaration
- Date: Mon, 12 May 2025 21:08:55 -0700
- Archived-at: <https://zsh.org/workers/53602>
- List-id: <zsh-workers.zsh.org>
Before we start wandering into anything else ...
On Mon, May 12, 2025 at 9:19 AM Philippe Altherr
<philippe.altherr@xxxxxxxxx> wrote:
>
> I think that "relative to definition place" rather than "relative to initialization place" makes so much more sense for -u that we should definitely adopt it.
Attached patch implements this and includes a doc update, though more
might be needed. Also fixes and introduces a test for the introduced
bug with "typeset -nu ref=nonexistent"
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
Messages sorted by:
Reverse Date,
Date,
Thread,
Author