Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: [BUG & tentative PATCH] Invalid call to upscope in createparam



But that's exactly what's not supposed to happen.  "typeset -nu"
references are allowed to create globals, "typeset -n" references are
not (they can only update what already exists).

?!? Since when is that the case? The current HEAD and the test release both create global variables when assigning to a reference that refers a not-yet-defined variable:

function () {
  typeset -n ref=var
  ref=foo
  typeset -p ref var
}
typeset -p var


Output:
typeset -n ref=var
typeset -g var=foo
typeset var=foo

There is no explicit documentation of that case but the following statement from section 14.3.2 strongly suggests that for non-upper references assignment to not-yet-defined should work.

To force a named reference to refer to the outer scope, even if a local has already been declared, add the -u option when declaring the named reference. In this case rname should already exist in the outer scope, otherwise the behavior of assignment through pname is not defined and may change the scope of the reference or fail with a status of 1.

I have no clue what is meant with "and may change the scope of the reference". Does that mean that the variable should be created in the local scope? If yes, which one? The current one, the one where the reference was initialized, or the one in which the reference was defined? In any case, the current implementation seems to always create the variable in the global scope (which is also what ksh does).

There is also this statement in the same section, which again strongly suggests that for non-subscripted references a variable should be created.

Unlike ‘(P)’, named references in substitutions that perform assignment, such as ${pname::=word}, do not create new arrays when rname is in the form of an array element or slice and no such array (or associative array) is presently set.

If non-upper references are not supposed to create variables then I have to ask why. Why should there be such a difference between -n and -nu references? I don't see anything in the documentation that points to such a difference, quite on the contrary (see above).

No.  The "title" of that test is "local nameref may not in-scope a
global parameter" -- a "typeset -n" reference can only refer to an
object that is either already in scope, or that later comes into the
namref's local scope by other means.

Here again, I have to admit that I have no clue of what is meant with "may not in-scope a global parameter" but I have a hard time to equate this to "references can't create (global) variables".
 
This should not happen, because unless the previous function has gone
wrong, there is no global zz variable.  The second function is there
to check that the first one did NOT create a global parameter.

A simple "typeset -p zz" at the top-level would be an easier way to check that there is no global "zz". As suggested by the comment below the test title ("previously this could create an infinite recursion and crash"), I suspect that the test is there for some other reason than (just) testing the creation of global variables.

Regarding your 5 examples, I agree with all of them, but none of them address the case present in the two examples related to the current bug.

More importantly, even if it were true that non-upper references are not supposed to create variables (or global variables), it wouldn't change the fact that the "upscope" under discussion is bogus. In my original example, it doesn't prevent the creation of a global variable. It only causes the creation of the wrong global variable, namely "var1" instead of "var2".

Philippe



On Mon, Jun 2, 2025 at 6:45 PM Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx> wrote:
On Mon, Jun 2, 2025 at 1:56 AM Philippe Altherr
<philippe.altherr@xxxxxxxxx> wrote:
>
> Here is the test example with comments showing what I think should happen:
>
> bar=xx
> typeset -n foo=bar
> () {
>   typeset -n foo;   # Creates a local "foo" placeholder ref.
>   foo=zz;           # Initializes the local "foo" placeholder ref with "zz" (the name of a not-yet-defined variable).
>   foo=zz || print -u2 foo: assignment failed;   # Creates the global variable "zz" and initializes it with "zz".
>   print $bar $zz;   # Prints the content of the global variable "bar" and "zz", which results in "xx zz".
> }

No.  The "title" of that test is "local nameref may not in-scope a
global parameter" -- a "typeset -n" reference can only refer to an
object that is either already in scope, or that later comes into the
namref's local scope by other means.

> () {
>   typeset -n foo;   # Creates a local "foo" placeholder ref.
>   foo=zz;           # Initializes the local "foo" placeholder ref with a reference to the global "zz" variable.

This should not happen, because unless the previous function has gone
wrong, there is no global zz variable.  The second function is there
to check that the first one did NOT create a global parameter.

>   local zz;         # Creates a local "zz" variable initialized with "".
>   foo=zz;           # Assigns "zz" to the global variable "zz".
>   print $bar $zz;   # Prints the content of the global variable "bar" and the local variable "zz", which results in "xx".
> }

So this is OK and assigns to the global:
() {
  typeset -n ref=gg
  typeset -g gg
  ref=assign1
}

And this is the same:
typeset gg
() {
  typeset -n ref=gg
  typeset gg
  ref=assign2
}

But this must use the local:
() {
  typeset -n ref=gg
  typeset gg
  ref=assign3
}
because at this point the local variable gg exists and no global can
be created "above" it.

This is OK and creates a global, because -u explicitly refers to the
surrounding scope:
() {
  typeset -nu ref=gg
  ref=assign4
}

But this has to fail because the local prevents creation of the global:
() {
  typeset -nu ref=gg
  typeset gg
  ref=assign5
}

Presently "ref=assign5" silently returns $?=1 in that case, but  I
intend to change that.


Messages sorted by: Reverse Date, Date, Thread, Author