zsh-workers
 help / color / mirror / code / Atom feed
* [PATCH] Fix crash on unset-through-nameref
@ 2024-03-04  5:37 Bart Schaefer
  2024-03-04  6:29 ` Stephane Chazelas
  0 siblings, 1 reply; 17+ messages in thread
From: Bart Schaefer @ 2024-03-04  5:37 UTC (permalink / raw)
  To: Zsh hackers list

[-- Attachment #1: Type: text/plain, Size: 359 bytes --]

On Sun, Mar 3, 2024 at 2:58 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> On Sun, Mar 3, 2024 at 12:27 PM Stephane Chazelas <stephane@chazelas.org> wrote:
> >
> > It segfaults with zsh -x though
>
> I'll have a look at that later, thanks.

Variation of the same problem reported by Jun T. when unsetting the
array part of a tied pair.

[-- Attachment #2: unset-via-nameref.txt --]
[-- Type: text/plain, Size: 1756 bytes --]

diff --git a/Src/builtin.c b/Src/builtin.c
index 83144677b..b867ea094 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -3935,9 +3935,11 @@ bin_unset(char *name, char **argv, Options ops, int func)
 		int ref = (pm->node.flags & PM_NAMEREF);
 		if (!(pm = (Param)resolve_nameref(pm, NULL)))
 		    continue;
-		if (ref && pm->level < locallevel) {
+		if (ref && pm->level < locallevel &&
+		    !(pm->node.flags & PM_READONLY)) {
 		    /* Just mark unset, do not remove from table */
-		    pm->node.flags |= PM_DECLARED|PM_UNSET;
+		    stdunsetfn(pm, 0);
+		    pm->node.flags |= PM_DECLARED;
 		    continue;
 		}
 	    }
diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst
index ff48e2289..5c123f122 100644
--- a/Test/K01nameref.ztst
+++ b/Test/K01nameref.ztst
@@ -669,6 +669,31 @@ F:Checking for a bug in zmodload that affects later tests
 >typeset -n ref=two
 >typeset -n ref=var
 
+ typeset -g .K01.scalar='RW'
+ typeset -gA .K01.assoc=(x y)
+ typeset -ga .K01.array=(z)
+ typeset -gi .K01.integer=0
+ typeset -gE .K01.double=0.0
+ typeset -gF .K01.float=0.0
+ typeset -gr .K01.readonly='RO'
+ typeset -n gref
+ for gref in ARGC .K01.{scalar,assoc,array,integer,double,float,readonly}
+ do
+   { unset gref } always { TRY_BLOCK_ERROR=0 }
+ done
+ typeset -p .K01.{scalar,assoc,array,integer,double,float,readonly}
+ unset .K01.{scalar,assoc,array,integer,double,float}
+0:unset various types via nameref, including a readonly special
+>typeset -g .K01.scalar
+>typeset -g -A .K01.assoc
+>typeset -g -a .K01.array
+>typeset -g -i .K01.integer
+>typeset -g -E .K01.double
+>typeset -g -F .K01.float
+>typeset -g -r .K01.readonly=RO
+*?*read-only variable: ARGC
+*?*read-only variable: .K01.readonly
+
  unset -n ref
  unset one
  typeset -n ref

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Fix crash on unset-through-nameref
  2024-03-04  5:37 [PATCH] Fix crash on unset-through-nameref Bart Schaefer
@ 2024-03-04  6:29 ` Stephane Chazelas
  2024-03-04  8:39   ` Bart Schaefer
  0 siblings, 1 reply; 17+ messages in thread
From: Stephane Chazelas @ 2024-03-04  6:29 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

2024-03-03 21:37:34 -0800, Bart Schaefer:
> On Sun, Mar 3, 2024 at 2:58 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
> >
> > On Sun, Mar 3, 2024 at 12:27 PM Stephane Chazelas <stephane@chazelas.org> wrote:
> > >
> > > It segfaults with zsh -x though
> >
> > I'll have a look at that later, thanks.
> 
> Variation of the same problem reported by Jun T. when unsetting the
> array part of a tied pair.
[...]

Thanks. That seems to fix it.

There's however a behaviour I can't explain:

$ ./Src/zsh -c 'f() { typeset -n v=$1; unset v; typeset -p w; v=(); }; w=1; f w; typeset -p w'
typeset -g w
typeset -a w=(  )
$ ./Src/zsh -c 'f() { typeset -n v=$1; unset v; typeset -p w; v=(); }; v=1; w=1; f w; typeset -p w'
typeset -g w
f: w: attempt to assign array value to non-array

The behaviour of that v=() seems to be influenced by the fact
that v was declared as scalar in the global scope.

See also:

$ ./Src/zsh -c 'myunset() { typeset -n v=$1; unset v; }; export x=1; myunset x; typeset -p x; x=2; typeset -p x'
export x
export x=2

That shows an unset through a nameref in a different scope
doesn't do a full unset: the variable remains declared and
retains its attributes (also evidenced by that "typeset -p w").

-- 
Stephane


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Fix crash on unset-through-nameref
  2024-03-04  6:29 ` Stephane Chazelas
@ 2024-03-04  8:39   ` Bart Schaefer
  2024-03-04 19:34     ` Stephane Chazelas
  0 siblings, 1 reply; 17+ messages in thread
From: Bart Schaefer @ 2024-03-04  8:39 UTC (permalink / raw)
  To: Zsh hackers list

On Sun, Mar 3, 2024 at 10:29 PM Stephane Chazelas <stephane@chazelas.org> wrote:
>
> That shows an unset through a nameref in a different scope
> doesn't do a full unset

Try removing this line from the patch:

+    pm->node.flags |= PM_DECLARED;

If that works, it'll also require updating the test.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Fix crash on unset-through-nameref
  2024-03-04  8:39   ` Bart Schaefer
@ 2024-03-04 19:34     ` Stephane Chazelas
  2024-03-04 19:36       ` Stephane Chazelas
  2024-03-04 23:18       ` Bart Schaefer
  0 siblings, 2 replies; 17+ messages in thread
From: Stephane Chazelas @ 2024-03-04 19:34 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

2024-03-04 00:39:30 -0800, Bart Schaefer:
> On Sun, Mar 3, 2024 at 10:29 PM Stephane Chazelas <stephane@chazelas.org> wrote:
> >
> > That shows an unset through a nameref in a different scope
> > doesn't do a full unset
> 
> Try removing this line from the patch:
> 
> +    pm->node.flags |= PM_DECLARED;
> 
> If that works, it'll also require updating the test.

That helps a bit but:

$ ./Src/zsh -c 'myunset() { typeset -n v=$1; unset v; }; export x=1; myunset x; typeset -p x; x=2; typeset -p x'
export x=2

It still retained its export attribute.

-- 
Stephane



^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Fix crash on unset-through-nameref
  2024-03-04 19:34     ` Stephane Chazelas
@ 2024-03-04 19:36       ` Stephane Chazelas
  2024-03-04 23:50         ` Bart Schaefer
  2024-03-04 23:18       ` Bart Schaefer
  1 sibling, 1 reply; 17+ messages in thread
From: Stephane Chazelas @ 2024-03-04 19:36 UTC (permalink / raw)
  To: Bart Schaefer, Zsh hackers list

2024-03-04 19:34:09 +0000, Stephane Chazelas:
> 2024-03-04 00:39:30 -0800, Bart Schaefer:
> > On Sun, Mar 3, 2024 at 10:29 PM Stephane Chazelas <stephane@chazelas.org> wrote:
> > >
> > > That shows an unset through a nameref in a different scope
> > > doesn't do a full unset
> > 
> > Try removing this line from the patch:
> > 
> > +    pm->node.flags |= PM_DECLARED;
> > 
> > If that works, it'll also require updating the test.
> 
> That helps a bit but:
> 
> $ ./Src/zsh -c 'myunset() { typeset -n v=$1; unset v; }; export x=1; myunset x; typeset -p x; x=2; typeset -p x'
> export x=2
> 
> It still retained its export attribute.
[...]

The other one is now wrong in a different way:

$ ./Src/zsh -c 'f() { typeset -n v=$1; unset v; typeset -p w; v=(a b); }; w=1; f w; typeset -p w'
typeset -a w=( a b )
$ ./Src/zsh -c 'f() { typeset -n v=$1; unset v; typeset -p w; v=(a b); }; v=1; w=1; f w; typeset -p w'
$

-- 
Stephane


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Fix crash on unset-through-nameref
  2024-03-04 19:34     ` Stephane Chazelas
  2024-03-04 19:36       ` Stephane Chazelas
@ 2024-03-04 23:18       ` Bart Schaefer
  2024-03-05  8:18         ` Stephane Chazelas
  1 sibling, 1 reply; 17+ messages in thread
From: Bart Schaefer @ 2024-03-04 23:18 UTC (permalink / raw)
  To: Bart Schaefer, Zsh hackers list

On Mon, Mar 4, 2024 at 11:34 AM Stephane Chazelas <stephane@chazelas.org> wrote:
>
> 2024-03-04 00:39:30 -0800, Bart Schaefer:
> >
> > Try removing this line from the patch:
> >
> > +    pm->node.flags |= PM_DECLARED;
>
> $ ./Src/zsh -c 'myunset() { typeset -n v=$1; unset v; }; export x=1; myunset x; typeset -p x; x=2; typeset -p x'
> export x=2
>
> It still retained its export attribute.

This gets messy because POSIX says that's exactly what's supposed to
happen when unsetting an export, even though in native zsh it normally
doesn't work that way.

You'll note it also didn't say
  typeset: no such variable: x

... because there is no provision in the parameter implementation for
a global to occupy a table slot and yet to appear not to exist.

This is one of the reasons your desire for "create it an as unset at
the time a nameref mentions it" has inherent problems.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Fix crash on unset-through-nameref
  2024-03-04 19:36       ` Stephane Chazelas
@ 2024-03-04 23:50         ` Bart Schaefer
  2024-03-05  8:36           ` Stephane Chazelas
  0 siblings, 1 reply; 17+ messages in thread
From: Bart Schaefer @ 2024-03-04 23:50 UTC (permalink / raw)
  To: Bart Schaefer, Zsh hackers list

On Mon, Mar 4, 2024 at 11:36 AM Stephane Chazelas <stephane@chazelas.org> wrote:
>
> The other one is now wrong in a different way:
>
> $ ./Src/zsh -c 'f() { typeset -n v=$1; unset v; typeset -p w; v=(a b); }; w=1; f w; typeset -p w'
> typeset -a w=( a b )
> $ ./Src/zsh -c 'f() { typeset -n v=$1; unset v; typeset -p w; v=(a b); }; v=1; w=1; f w; typeset -p w'
> $

Src/zsh -c 'f() { typeset -n v=$1; unset v; typeset -p w; v=(a b) ||
echo failed; }; v=1; w=1; f w; typeset -p w'
failed

So removing the PM_DECLARED is hiding the "attempt to assign array
value to non-array" message but still not permitting the assignment.

This probably is not better than the previous situation.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Fix crash on unset-through-nameref
  2024-03-04 23:18       ` Bart Schaefer
@ 2024-03-05  8:18         ` Stephane Chazelas
  2024-03-05 18:42           ` Bart Schaefer
  0 siblings, 1 reply; 17+ messages in thread
From: Stephane Chazelas @ 2024-03-05  8:18 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

2024-03-04 15:18:06 -0800, Bart Schaefer:
> On Mon, Mar 4, 2024 at 11:34 AM Stephane Chazelas <stephane@chazelas.org> wrote:
> >
> > 2024-03-04 00:39:30 -0800, Bart Schaefer:
> > >
> > > Try removing this line from the patch:
> > >
> > > +    pm->node.flags |= PM_DECLARED;
> >
> > $ ./Src/zsh -c 'myunset() { typeset -n v=$1; unset v; }; export x=1; myunset x; typeset -p x; x=2; typeset -p x'
> > export x=2
> >
> > It still retained its export attribute.
> 
> This gets messy because POSIX says that's exactly what's supposed to
> happen when unsetting an export, even though in native zsh it normally
> doesn't work that way.

No, I think you're confusing with:

export foo

Which is meant to give the export attribute to foo without
giving it a value (so the variable will end up in the
environment of executed command if ever its assigned in the
future but not until then).

unset foo (or better unset -v foo as POSIX allows bash's
behaviour where that could unset a function instead) is required
to clear the value and export attribute.

unset is the only way to unexport a variable in POSIX shells.


> You'll note it also didn't say
>   typeset: no such variable: x
> 
> ... because there is no provision in the parameter implementation for
> a global to occupy a table slot and yet to appear not to exist.
> 
> This is one of the reasons your desire for "create it an as unset at
> the time a nameref mentions it" has inherent problems.

I was suggesting a "unset (and undeclared then) but named ref"
(and refcount if needed for garbage collection) flag for that in
a separate message, but I have no idea whether that'd be
sufficient or feasible.

I hate to say this but it seems to me that if this kind of issue
is not fixable, then it would likely be preferable (from a
consistency PoV at least) to go for bash/mksh dumber approaches
where namerefs are just plain scalar variables containing the
name of another variable (or other lvalue) and having the target
variable resolved any time the nameref is assigned/referenced
(in whatever scope that happens to be).

You'd still need to namespace your variables in functions using
namerefs, even more so, but at least it would be more consistent
and we wouldn't have to list all the special cases that work or
don't work in the documentation

BTW, speaking of "other lvalue", see also:

$ ksh -c 'typeset -n a="b[n++]"; typeset -p n; b=(); a=a a=b a=c; typeset -p b n'
n=2
typeset -a b=([1]=c)
n=2
$ mksh -c 'typeset -n a="b[n++]"; typeset -p n; b=(); a=a a=b a=c; typeset -p b n'
set -A b
typeset b[0]=a
typeset b[2]=b
typeset b[4]=c
typeset n=6
$ bash -c 'typeset -n a="b[n++]"; typeset -p n; b=(); a=a a=b a=c; typeset -p b n'
bash: line 1: typeset: n: not found
declare -a b=([0]="a" [1]="b" [2]="c")
declare -- n="3"
$ ./Src/zsh -c 'typeset -n a="b[n++]"; typeset -p n; b=(); a=a a=b a=c; typeset -p b n'
zsh:typeset:1: no such variable: n
typeset -a b=( a '' b '' c )
typeset -i n=6

Which suggests the lvalue is still evaluated by zsh at every assignment at
least.

(see also
https://unix.stackexchange.com/questions/640687/prevent-command-substitution-from-running-when-declaring-a-variable/640695#640695
where namerefs are abused to have a variable with a dynamic
value).

-- 
Stephane


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Fix crash on unset-through-nameref
  2024-03-04 23:50         ` Bart Schaefer
@ 2024-03-05  8:36           ` Stephane Chazelas
  0 siblings, 0 replies; 17+ messages in thread
From: Stephane Chazelas @ 2024-03-05  8:36 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

2024-03-04 15:50:45 -0800, Bart Schaefer:
> On Mon, Mar 4, 2024 at 11:36 AM Stephane Chazelas <stephane@chazelas.org> wrote:
> >
> > The other one is now wrong in a different way:
> >
> > $ ./Src/zsh -c 'f() { typeset -n v=$1; unset v; typeset -p w; v=(a b); }; w=1; f w; typeset -p w'
> > typeset -a w=( a b )
> > $ ./Src/zsh -c 'f() { typeset -n v=$1; unset v; typeset -p w; v=(a b); }; v=1; w=1; f w; typeset -p w'
> > $
> 
> Src/zsh -c 'f() { typeset -n v=$1; unset v; typeset -p w; v=(a b) ||
> echo failed; }; v=1; w=1; f w; typeset -p w'
> failed
> 
> So removing the PM_DECLARED is hiding the "attempt to assign array
> value to non-array" message but still not permitting the assignment.
> 
> This probably is not better than the previous situation.

But it works in:

$ zsh -c 'a=0; f() { local a=1; a[2]=(foo bar); typeset -p a; }; f; typeset -p a'
f: a: attempt to assign array value to non-array
$ zsh -c 'a=0; f() { local a=1; unset -v a; a[2]=(foo bar); typeset -p a; }; f; typeset -p a'
typeset -a a=( '' foo bar )
typeset a=0

Where we should also have a variable after that unset that is not
set but "declared" without any type (not in the global scope
though), suggesting the problem may be fixable.

-- 
Stephane


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Fix crash on unset-through-nameref
  2024-03-05  8:18         ` Stephane Chazelas
@ 2024-03-05 18:42           ` Bart Schaefer
  2024-03-05 19:38             ` Stephane Chazelas
                               ` (2 more replies)
  0 siblings, 3 replies; 17+ messages in thread
From: Bart Schaefer @ 2024-03-05 18:42 UTC (permalink / raw)
  To: Bart Schaefer, Zsh hackers list

On Tue, Mar 5, 2024 at 12:19 AM Stephane Chazelas <stephane@chazelas.org> wrote:
>
> 2024-03-04 15:18:06 -0800, Bart Schaefer:
> > On Mon, Mar 4, 2024 at 11:34 AM Stephane Chazelas <stephane@chazelas.org> wrote:
> > >
> > > It still retained its export attribute.
> >
> > This gets messy because POSIX says that's exactly what's supposed to
> > happen when unsetting an export, even though in native zsh it normally
> > doesn't work that way.
>
> No, I think you're confusing with:
>
> export foo

I'm referring to comments in the C code, e.g.:

     * POSIXBUILTINS horror: we need to retain the 'readonly' or 'export'
     * flags of an unset parameter.
[...]
    if (isset(POSIXBUILTINS)) {
[...]
        /* This is handled by createparam():
        if (usepm && (pm->node.flags & PM_EXPORTED) && !(off & PM_EXPORTED))
            on |= PM_EXPORTED;
        */

> I hate to say this but it seems to me that if this kind of issue
> is not fixable, then it would likely be preferable (from a
> consistency PoV at least) to go for bash/mksh dumber approaches
> where namerefs are just plain scalar variables containing the
> name of another variable (or other lvalue) and having the target
> variable resolved any time the nameref is assigned/referenced

That is in fact exactly how it works, with the addition now that -u
makes it skip upward one scope before resolving.

> (see also
> https://unix.stackexchange.com/questions/640687/prevent-command-substitution-from-running-when-declaring-a-variable/640695#640695
> where namerefs are abused to have a variable with a dynamic
> value).

Although I wasn't aware of that discussion, I had already thought of that:

  When RNAME includes an array subscript, the subscript expression is
  interpreted at the time ${PNAME} is expanded.  Any form of subscript is
  allowed, including those that select individual elements, substrings of
  scalar strings, or multiple elements as with array slices or the '(i)',
  '(I)', '(r)', '(R)' and '(w)' subscript flags.  However, the subscript
  is evaluated with the NO_EXEC option in effect, so command substitution
  and other similar constructs produce no output, although are not
  syntactically excluded.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Fix crash on unset-through-nameref
  2024-03-05 18:42           ` Bart Schaefer
@ 2024-03-05 19:38             ` Stephane Chazelas
  2024-03-05 23:16               ` Bart Schaefer
  2024-03-05 19:48             ` unset, POSIX and the export attribute (Was: [PATCH] Fix crash on unset-through-nameref) Stephane Chazelas
  2024-03-05 19:51             ` [PATCH] Fix crash on unset-through-nameref Stephane Chazelas
  2 siblings, 1 reply; 17+ messages in thread
From: Stephane Chazelas @ 2024-03-05 19:38 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

2024-03-05 10:42:18 -0800, Bart Schaefer:
[...]
> > I hate to say this but it seems to me that if this kind of issue
> > is not fixable, then it would likely be preferable (from a
> > consistency PoV at least) to go for bash/mksh dumber approaches
> > where namerefs are just plain scalar variables containing the
> > name of another variable (or other lvalue) and having the target
> > variable resolved any time the nameref is assigned/referenced
> 
> That is in fact exactly how it works, with the addition now that -u
> makes it skip upward one scope before resolving.
[...]

Well, not in the:

$ ./Src/zsh -c 'f() { typeset -n ref=var; local var=1; echo $ref; }; var=0; f'
0

Which IMO is better than 

$ bash -c 'f() { typeset -n ref=var; local var=1; echo $ref; }; var=0; f'
1
$ mksh -c 'f() { typeset -n ref=var; local var=1; echo $ref; }; var=0; f'
1

But is IMO somewhat inconsistent with:

$ ./Src/zsh -c 'f() { nameref ref=var; local var=1; echo $ref; }; f'
1

-- 
Stephane


^ permalink raw reply	[flat|nested] 17+ messages in thread

* unset, POSIX and the export attribute (Was: [PATCH] Fix crash on unset-through-nameref)
  2024-03-05 18:42           ` Bart Schaefer
  2024-03-05 19:38             ` Stephane Chazelas
@ 2024-03-05 19:48             ` Stephane Chazelas
  2024-03-05 19:51             ` [PATCH] Fix crash on unset-through-nameref Stephane Chazelas
  2 siblings, 0 replies; 17+ messages in thread
From: Stephane Chazelas @ 2024-03-05 19:48 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

2024-03-05 10:42:18 -0800, Bart Schaefer:
> On Tue, Mar 5, 2024 at 12:19 AM Stephane Chazelas <stephane@chazelas.org> wrote:
> >
> > 2024-03-04 15:18:06 -0800, Bart Schaefer:
> > > On Mon, Mar 4, 2024 at 11:34 AM Stephane Chazelas <stephane@chazelas.org> wrote:
> > > >
> > > > It still retained its export attribute.
> > >
> > > This gets messy because POSIX says that's exactly what's supposed to
> > > happen when unsetting an export, even though in native zsh it normally
> > > doesn't work that way.
> >
> > No, I think you're confusing with:
> >
> > export foo
> 
> I'm referring to comments in the C code, e.g.:
> 
>      * POSIXBUILTINS horror: we need to retain the 'readonly' or 'export'
>      * flags of an unset parameter.
> [...]
>     if (isset(POSIXBUILTINS)) {
> [...]
>         /* This is handled by createparam():
>         if (usepm && (pm->node.flags & PM_EXPORTED) && !(off & PM_EXPORTED))
>             on |= PM_EXPORTED;
>         */
[...]

But AFAICT, that's about the "export var" when var is not
previously set. You can't unset a readonly variable anyway.

zsh retaining the export attribute of a variable upon unset
would make it non-compliant.

POSIX is not ambiguous on that. See
https//pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/V3_chap02.html#unset

"unset - unset values and attributes of variables and functions"
"Read-only variables cannot be unset."

-- 
Stephane


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Fix crash on unset-through-nameref
  2024-03-05 18:42           ` Bart Schaefer
  2024-03-05 19:38             ` Stephane Chazelas
  2024-03-05 19:48             ` unset, POSIX and the export attribute (Was: [PATCH] Fix crash on unset-through-nameref) Stephane Chazelas
@ 2024-03-05 19:51             ` Stephane Chazelas
  2 siblings, 0 replies; 17+ messages in thread
From: Stephane Chazelas @ 2024-03-05 19:51 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

2024-03-05 10:42:18 -0800, Bart Schaefer:
[...]
> Although I wasn't aware of that discussion, I had already thought of that:
> 
>   When RNAME includes an array subscript, the subscript expression is
>   interpreted at the time ${PNAME} is expanded.  Any form of subscript is
>   allowed, including those that select individual elements, substrings of
>   scalar strings, or multiple elements as with array slices or the '(i)',
>   '(I)', '(r)', '(R)' and '(w)' subscript flags.  However, the subscript
>   is evaluated with the NO_EXEC option in effect, so command substitution
>   and other similar constructs produce no output, although are not
>   syntactically excluded.
[...]

Thanks for those details. Note the double evaluation though
(where n ends up incremented twice each time in my example)
which seems to be affecting all shells except bash.

-- 
Stephane


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Fix crash on unset-through-nameref
  2024-03-05 19:38             ` Stephane Chazelas
@ 2024-03-05 23:16               ` Bart Schaefer
  2024-03-06 18:21                 ` Stephane Chazelas
  0 siblings, 1 reply; 17+ messages in thread
From: Bart Schaefer @ 2024-03-05 23:16 UTC (permalink / raw)
  To: Bart Schaefer, Zsh hackers list

On Tue, Mar 5, 2024 at 11:38 AM Stephane Chazelas <stephane@chazelas.org> wrote:
>
> 2024-03-05 10:42:18 -0800, Bart Schaefer:
> [...]
> > > where namerefs are just plain scalar variables containing the
> > > name of another variable (or other lvalue) and having the target
> > > variable resolved any time the nameref is assigned/referenced
> >
> > That is in fact exactly how it works, with the addition now that -u
> > makes it skip upward one scope before resolving.
>
> Well, not in the:
>
> $ ./Src/zsh -c 'f() { typeset -n ref=var; local var=1; echo $ref; }; var=0; f'
> 0

OK, yes, there is one extra attempt to resolve at the time of
declaration, and it "remembers" the scope where it completed the
resolve and always goes first to that scope again.

> But is IMO somewhat inconsistent with:
>
> $ ./Src/zsh -c 'f() { nameref ref=var; local var=1; echo $ref; }; f'
> 1

Sorry, your example is confusing me.  "nameref" is only available when
the zsh/ksh93 module is loaded.  What's actually happening here, and
why do you never use -fc to prevent dotfiles from being read?

I think you're referring to the difference between
  var=0; f
and simply
  f
In the latter case, "the scope where it completed the resolve" is the
local scope because no parameter in a surrounding scope exists.  If
you assign to such a nameref, it "goes first to that scope", and if it
still finds nothing, climbs up to global scope (because implicitly
"finds nothing" at any local scope means there can't be that name at
global scope).

It's consistent with how $var / var=x would work if you never declared
anything, and consistent with ${(P)ptr} / ${(P)ptr::=x} when ptr is a
plain scalar.  The difference is when the ref has already found
something, which seems like the intended difference if you're using a
reference in the first place.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Fix crash on unset-through-nameref
  2024-03-05 23:16               ` Bart Schaefer
@ 2024-03-06 18:21                 ` Stephane Chazelas
  2024-03-06 19:17                   ` Bart Schaefer
  0 siblings, 1 reply; 17+ messages in thread
From: Stephane Chazelas @ 2024-03-06 18:21 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

2024-03-05 15:16:25 -0800, Bart Schaefer:
[...]
> Sorry, your example is confusing me.  "nameref" is only available when
> the zsh/ksh93 module is loaded.  What's actually happening here, and
> why do you never use -fc to prevent dotfiles from being read?

I hadn't realised -f also skipped ~/.zshenv, I thought that one
like /etc/zshenv could not be skipped (and why I always leave it
empty as it also affects scripts).

Should we also use

#! /bin/zsh -f-

shebangs in scripts?

> I think you're referring to the difference between
>   var=0; f
> and simply
>   f
> In the latter case, "the scope where it completed the resolve" is the
> local scope because no parameter in a surrounding scope exists.  If
> you assign to such a nameref, it "goes first to that scope", and if it
> still finds nothing, climbs up to global scope (because implicitly
> "finds nothing" at any local scope means there can't be that name at
> global scope).
> 
> It's consistent with how $var / var=x would work if you never declared
> anything, and consistent with ${(P)ptr} / ${(P)ptr::=x} when ptr is a
> plain scalar.  The difference is when the ref has already found
> something, which seems like the intended difference if you're using a
> reference in the first place.

My point is that if it means the:

assign() {
  typeset -n var=$1
  local value=$2
  var=$value
}

doesn't work for

assign var value
or
assign value something

And we need to work around it by doing:

assign() {
  typeset -n _assign_var=$1
  local _assign_value=$2
  _assign_var=$_assign_value
}

(Yes, I know we can always fo assign() eval -- $1=\$2)

Like we do in bash/mksh or when using (P) or eval... then
there's little point trying to be smarter in the

var=; assign var value
value=; assign value something

cases. You may say there's no harm in doing so, I'd agree
there's little harm except
- it makes it a bit inconsistent
- it may trick users into thinking they can get away without
  using namespacing.

In any case, I'm not against being smart in the second case, I
just wish we could get away without the namespacing (like in the
zslurp case which started that whole discussion).

-- 
Stephane


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Fix crash on unset-through-nameref
  2024-03-06 18:21                 ` Stephane Chazelas
@ 2024-03-06 19:17                   ` Bart Schaefer
  2024-03-06 19:37                     ` Bart Schaefer
  0 siblings, 1 reply; 17+ messages in thread
From: Bart Schaefer @ 2024-03-06 19:17 UTC (permalink / raw)
  To: Zsh hackers list

On Wed, Mar 6, 2024 at 10:21 AM Stephane Chazelas <stephane@chazelas.org> wrote:
>
> 2024-03-05 15:16:25 -0800, Bart Schaefer:
> > why do you never use -fc to prevent dotfiles from being read?
>
> I hadn't realised -f also skipped ~/.zshenv
>
> Should we also use
>
> #! /bin/zsh -f-
>
> shebangs in scripts?

I usually assume scripts want ~/.zshenv for PATH values etc.  As usual
you should do what makes sense for the specific script.

> My point is that if it means the:
>
> assign() {
>   typeset -n var=$1
>   local value=$2
>   var=$value
> }
>
> doesn't work for
>
> assign var value
> or
> assign value something

No, but as of the last patch it works for both of those cases if you use

assign() {
  typeset -g $1
  typeset -nu var=$1
  local value=$2
  var=$value
}

You don't have to futz with local parameter names, but you do have to
make sure that the "typeset -g" declaration has a compatible type with
the "var=$value" at the end.

The "typeset -nu" declaration can't know what you're going to assign
later, so it doesn't make sense for it to try to magically perform the
"typeset -g".  We don't have a "typeless and waiting for assignment"
parameter type.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] Fix crash on unset-through-nameref
  2024-03-06 19:17                   ` Bart Schaefer
@ 2024-03-06 19:37                     ` Bart Schaefer
  0 siblings, 0 replies; 17+ messages in thread
From: Bart Schaefer @ 2024-03-06 19:37 UTC (permalink / raw)
  To: Zsh hackers list

On Wed, Mar 6, 2024 at 11:17 AM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> assign() {
>   typeset -g $1
>   typeset -nu var=$1
>   local value=$2
>   var=$value
> }

Of course there's also no reason to write that specific function that
way.  You can simply do

assign() {
  unset $1
  typeset -g $1=$2
}

The reason for using a named reference would be when you're going do
more things with $var and $value than just assign them, such as
calling another function, or when you want to do something like

append() {
  typeset -nu var=$1
  local value=$2
  # Maybe do something else to $value ?  Then:
  var+=$value
}

Mostly because "typeset" doesn't understand += syntax.


^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2024-03-06 19:38 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-04  5:37 [PATCH] Fix crash on unset-through-nameref Bart Schaefer
2024-03-04  6:29 ` Stephane Chazelas
2024-03-04  8:39   ` Bart Schaefer
2024-03-04 19:34     ` Stephane Chazelas
2024-03-04 19:36       ` Stephane Chazelas
2024-03-04 23:50         ` Bart Schaefer
2024-03-05  8:36           ` Stephane Chazelas
2024-03-04 23:18       ` Bart Schaefer
2024-03-05  8:18         ` Stephane Chazelas
2024-03-05 18:42           ` Bart Schaefer
2024-03-05 19:38             ` Stephane Chazelas
2024-03-05 23:16               ` Bart Schaefer
2024-03-06 18:21                 ` Stephane Chazelas
2024-03-06 19:17                   ` Bart Schaefer
2024-03-06 19:37                     ` Bart Schaefer
2024-03-05 19:48             ` unset, POSIX and the export attribute (Was: [PATCH] Fix crash on unset-through-nameref) Stephane Chazelas
2024-03-05 19:51             ` [PATCH] Fix crash on unset-through-nameref Stephane Chazelas

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).