Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
[PATCH] Re: nameref binding not finding calling-scope vars?
- X-seq: zsh-workers 53431
- From: Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx>
- To: Zsh Workers <zsh-workers@xxxxxxx>
- Subject: [PATCH] Re: nameref binding not finding calling-scope vars?
- Date: Mon, 31 Mar 2025 15:00:00 -0700
- Archived-at: <https://zsh.org/workers/53431>
- In-reply-to: <CAH+w=7YBt32KgzYv97op9yGG7SqYhxPiuhpcNAFZNfbdqpJLiw@mail.gmail.com>
- List-id: <zsh-workers.zsh.org>
- References: <Z-ee8qMpHlTDQkRh@fullerene.field.pennock-tech.net> <CAH+w=7YBt32KgzYv97op9yGG7SqYhxPiuhpcNAFZNfbdqpJLiw@mail.gmail.com>
On Sun, Mar 30, 2025 at 9:34 PM Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx> wrote:
>
> What's happening here is that the var-to-foo transition happens first,
> and then the -g flag moves up the chain from that point and alters the
> global. This is in fact a bug
Attached patch fixes this. Tests based on this thread included.
> This one I would have expected to work ... but it appears the same
> thing is happening here as in the typeset -g case, except this time it
> actually does catch the error.
This should work after the attached patch.
> > the docs as of commit a528af5c57 (2023-12-16) and now has discussion of
> > `-u` to find in the parent scope.
>
> That skips over a declaration of the referent name in the local scope,
As demonstrated by the "part 14" test in the attached patch, although
you can us a simple assignment through a -nu reference into the
calling scope, "typeset" has a different problem -- if there is a
parameter in the local scope with the same name as the referent,
"typeset" will always find that one, even given the -g option. This
is also the case without namerefs, that is, given ...
% inner () { local foo=inner; typeset -g foo=(a b c); typeset -p foo }
% outer() { local foo=(x y z); inner; typeset -p foo }
% outer
... you might expect ...
typeset foo=inner
typeset -a foo=( a b c )
... but what happens is ...
typeset -a foo=( a b c )
typeset -a foo=( x y z )
Even unsetting the local, once declared, won't get around this.
I've not yet prepared a documentation patch for the foregoing.
> It will NOT allow you to change the type of the referent by direct
> assignment. So how do you fix that? You explicitly unset it rather
> than rely on the implicit unset.
Test for this included as well.
diff --git a/Src/params.c b/Src/params.c
index d1c06b893..c10236a0d 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -6395,7 +6395,9 @@ setscope(Param pm)
}
} else
pm->base = basepm->level;
- }
+ } else if (pm->base < locallevel && refname &&
+ (basepm = (Param)getparamnode(realparamtab, refname)))
+ pm->base = basepm->level;
if (pm->base > pm->level) {
if (EMULATION(EMULATE_KSH)) {
zerr("%s: global reference cannot refer to local variable",
@@ -6420,6 +6422,8 @@ upscope(Param pm, int reflevel)
{
Param up = pm->old;
while (up && up->level >= reflevel) {
+ if (reflevel < 0 && up->level < locallevel)
+ break;
pm = up;
up = up->old;
}
diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst
index bacc3ade2..1603ab1b9 100644
--- a/Test/K01nameref.ztst
+++ b/Test/K01nameref.ztst
@@ -595,6 +595,53 @@ F:Same test, should part 5 output look like this?
>nameref-local-nameref-local
>typeset -h parameters
+ (
+ inner() { local -n var="${1:?}"; var=(alpha beta gamma); }
+ outer() { local -a foo=(outer); inner foo; typeset -p foo; }
+ foo=3 ; { outer foo } always { typeset -p foo }
+ )
+0:up-reference part 10, assignment to enclosing scope, types match
+>typeset -a foo=( alpha beta gamma )
+>typeset -g foo=3
+
+ (
+ inner() { local -n var="${1:?}"; var=(alpha beta gamma); }
+ outer() { local foo=outer; inner foo; typeset -p foo; }
+ foo=3 ; { outer foo } always { typeset -p foo }
+ )
+1:up-reference part 11, assignment to enclosing scope, type mismatch
+>typeset -g foo=3
+?inner: foo: attempt to assign array value to non-array
+
+ (
+ inner() { local -n var="${1:?}"; unset var; var=(alpha beta gamma); }
+ outer() { local foo=outer; inner foo; typeset -p foo; }
+ foo=3 ; { outer foo } always { typeset -p foo }
+ )
+0:up-reference part 12, assignment to enclosing scope, unset by reference
+>typeset -a foo=( alpha beta gamma )
+>typeset -g foo=3
+
+ (
+ inner() { local "${1:?}"; local -nu var="$1"; var=(alpha beta gamma); }
+ outer() { local -a foo=(outer); inner foo; typeset -p foo; }
+ foo=3 ; { outer foo } always { typeset -p foo }
+ )
+0:up-reference part 13, assignment to enclosing scope, skip local
+>typeset -a foo=( alpha beta gamma )
+>typeset -g foo=3
+
+ (
+ inner() { local "${1:?}"; local -nu var="$1";
+ typeset -g var=(alpha beta gamma); }
+ outer() { local -a foo=(outer); inner foo; typeset -p foo; }
+ foo=3 ; { outer foo } always { typeset -p foo }
+ )
+0f:up-reference part 14, typeset -g to enclosing scope, skip local
+F:typeset cannot bypass a name in the local scope, even via nameref
+>typeset -a foo=( alpha beta gamma )
+>typeset -g foo=3
+
if [[ $options[typesettounset] != on ]]; then
ZTST_skip='Ignoring zmodload bug that resets TYPESET_TO_UNSET'
setopt typesettounset
Messages sorted by:
Reverse Date,
Date,
Thread,
Author