Local and subroutines inside functions

Report problems with documented functionality
pneumatic
Posts: 182
Joined: 05 Dec 2016, 01:51

Local and subroutines inside functions

Post by pneumatic » 13 Feb 2018, 23:42

In the following example, the first click of the button shows the local var is blank.
On the second click, the previous value is remembered like a static.
Listvars reports it's not local, static or global.

Code: Select all

GUI()
return

GUI(){
global

	Gui , New
	Gui , Add , Button , x115 y10 gShowVar , Show local var
	Gui , Show , w300 h50
	return
	
	
	ShowVar:	
	local var
	MsgBox % var
	var := 1
	listvars
	return
}

The documentation says we "should" only use statics and globals, but it doesn't say that we can't. If ahk is automatically converting it to a static or global for me, it should show up as such in listvars, or in the documentation.
https://autohotkey.com/docs/Functions.htm#gosub wrote: A function may contain externally-called subroutines such as timers, GUI g-labels, and menu items. This is generally done to encapsulate them in a separate file for use with #Include, which prevents them from interfering with the script's auto-execute section. However, the following limitations apply:

* Such subroutines should use only static and global variables (not locals) if their function is ever called normally. This is because a subroutine thread that interrupts a function-call thread (or vice versa) would be able to change the values of local variables seen by the interrupted thread. Furthermore, any time a function returns to its caller, all of its local variables are made blank to free their memory.
This whole time I thought I was using local vars, and now I don't know what they are or whether it's causing bugs in my program!

I am hoping someone can tell me I have overlooked something and that this is not a bug.

Edit: while I'm here, may I ask a clarification about functions. If a function is interrupted by another thread's call to the same function (i.e directly, not from a subroutine within the function), would this mean that when the first thread is handed back execution, it's local vars will be what the interrupting thread left them as? i.e there are NOT two copies of the local var in memory (one for each thread's instance of the function) but rather just the one copy of local var which both threads share. Thanks!
User avatar
nnnik
Posts: 3329
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Local behaving as static

Post by nnnik » 14 Feb 2018, 03:37

It's behaving as global because your Label is global - they always are.
Recommends AHK Studio
pneumatic
Posts: 182
Joined: 05 Dec 2016, 01:51

Re: Local behaving as static

Post by pneumatic » 14 Feb 2018, 07:02

But the documentation says a subroutine within a function can use local (static) vars. Well actually, the documentation I quoted above says statics are "not locals", but elsewhere it says "Static variables are always implicitly local". But then listvars says it's not global. Anyway, is this not a case of a problem with documented functionality?
User avatar
Masonjar13
Posts: 1395
Joined: 20 Jul 2014, 10:16
GitHub: Masonjar13
Location: Не Россия

Re: Local behaving as static

Post by Masonjar13 » 14 Feb 2018, 18:31

There appears to be fault on both sides. The primary fault is your paradigm. Putting a label in a function is never a good idea in the first place, and only should be done if it works completely independent from the function and function variables. By sending the script to the label directly, you're bypassing the actual function call, which technically should create a separate function space for the time its running. You'll notice that if you check a_thisFunc where the var check is, it will return nothing, meaning it's not technically in the function. On the other hand, static variables that are instantiated in the function will be available at the label call. So, what I believe is happening is it's running in the space where the static variables for the function are, making local and static synonymous.

While you could perhaps call that an interpreter bug, I would call that user error. Ultimately, you should not use labels in functions. If you wish to have temporary vars for a global GUI (doesn't make any real sense), you can direct the g-label to another function.
Helgef
Posts: 3190
Joined: 17 Jul 2016, 01:02
Contact:

Re: Local behaving as static

Post by Helgef » 15 Feb 2018, 02:56

The variable var is local, not static and not global. When you click the button, you have access to function's (one and only) variable space. The return (of the gui thread) doesn't belong to the function, hence the function's local variables aren't made blank.

You could argue that listlines should show the function's variable space when accessed via the subroutine.

Cheers.
User avatar
nnnik
Posts: 3329
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Local behaving as static

Post by nnnik » 15 Feb 2018, 06:03

@Helgef I think your explination is quite confusing. I will try to come up with my own:
The variable is indeed local. However unlike other local vars you are accessing it while outside of the actual function and write to it from outside the actual function.
Therefore after your variable is created and your subroutine is left nothing happens. Normally when you leave a function ( and only then ) all local variables are freed.
However since you never leave the function but only a related subroutine the local variables aren't freed causing them to behave like static variables.
This can be solved either by releasing the local variables when you leave the subroutine or by prohibiting local in it.

This seems to be a valid bug report therefore back to Bug report forums.
Recommends AHK Studio
Helgef
Posts: 3190
Joined: 17 Jul 2016, 01:02
Contact:

Re: Local and subroutines inside functions

Post by Helgef » 15 Feb 2018, 06:35

@nnnik, it is not a bug, it is a useless feature though (jumping into a function from outside), as hinted by Masonjar13.
However, the following limitations apply:
a subroutine thread that interrupts a function-call thread (or vice versa) would be able to change the values of local variables seen by the interrupted thread. Furthermore, any time a function returns to its caller, all of its local variables are made blank to free their memory.
From Using Subroutines Within a Function.
As decribed above, entering the function from a GUI g-label enables you to modify the function's local variables, and when the function returns to its caller the variables are made blank, not otherwise (i.e., not when the GUI g-label routine finishes).
This can be solved either by releasing the local variables when you leave the subroutine or by prohibiting local in it.
No it cannot, that is a not a backwards compatible change.

Cheers.
V2
User avatar
nnnik
Posts: 3329
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Local and subroutines inside functions

Post by nnnik » 15 Feb 2018, 06:43

Helgef wrote:@nnnik, it is not a bug, it is a useless feature though (jumping into a function from outside), as hinted by Masonjar13.
However, the following limitations apply:
a subroutine thread that interrupts a function-call thread (or vice versa) would be able to change the values of local variables seen by the interrupted thread. Furthermore, any time a function returns to its caller, all of its local variables are made blank to free their memory.
From Using Subroutines Within a Function.
As decribed above, entering the function from a GUI g-label enables you to modify the function's local variables, and when the function returns to its caller the variables are made blank, not otherwise (i.e., not when the GUI g-label routine finishes).
From what I see the piece of Text from above describes the case where a function is running and interupted - our function is neither running nor interrupted. We have a function that isn't running in any thread.
Whether it's useful or not is secondary. It's completely inconsistent and should either work consistent or hint that to the user ( e. g. throwing an error )

This can be solved either by releasing the local variables when you leave the subroutine or by prohibiting local in it.
No it cannot, that is a not a backwards compatible change.
I don't know if any of the changes I described can be made possible I just described how a fix could work.
Recommends AHK Studio
Helgef
Posts: 3190
Joined: 17 Jul 2016, 01:02
Contact:

Re: Local and subroutines inside functions

Post by Helgef » 15 Feb 2018, 07:30

From what I see the piece of Text from above describes the case where a function is running and interupted
It implies that the subroutine is using the function's variables. It describes when the variables are cleared, which imples they are not cleared in other cases. Variables cannot belong to a label. If you clear variables when the subroutine ends, you get inconsistent results when subroutines are interrupting eachother. In all cases, this is a useless feature (imho ;) ).
I just described how a fix could work.
Noted.

Cheers.
User avatar
Masonjar13
Posts: 1395
Joined: 20 Jul 2014, 10:16
GitHub: Masonjar13
Location: Не Россия

Re: Local and subroutines inside functions

Post by Masonjar13 » 15 Feb 2018, 20:20

Well, I was way off then. But it does say "when it interrupts a function thread." As nnnik said, it's not actually running, so there is no actual thread (unless logical threads still exist after they've finished? I thought they just had a sub-global namespace), which makes the documentation ambiguous. I think clarification in the documentation should be made.
Helgef
Posts: 3190
Joined: 17 Jul 2016, 01:02
Contact:

Re: Local and subroutines inside functions

Post by Helgef » 16 Feb 2018, 02:42

I think clarification in the documentation should be made.
I'm not opposed that, I suggest bold,
However, the following limitations apply:
a subroutine thread that interrupts a function-call thread (or vice versa) would be able to change the values of local variables seen by the interrupted thread. Furthermore, any time a function returns to its caller, all of its local variables are made blank to free their memory.
This implies that a subroutine is using the function's local variables, and an interrupting function call would be able to change the local variables seen by the subroutine. And they aren't cleared until a function call returns to its caller. Consequently, if there is no interrupting function call it doesn't change the local variables seen by the subroutine, and there is no function call returning, hence, they aren't made blank. As observed by the op.

Cheers.
User avatar
nnnik
Posts: 3329
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Local and subroutines inside functions

Post by nnnik » 16 Feb 2018, 03:01

I think it should explicitly mention that locals don't get cleared inside the subroutine
Recommends AHK Studio
Helgef
Posts: 3190
Joined: 17 Jul 2016, 01:02
Contact:

Re: Local and subroutines inside functions

Post by Helgef » 16 Feb 2018, 06:48

I agree nnnik. In general, when there is a discussion involving multiple interpretations about something in the documentation, it needs explicit clarification.

Cheers.
lexikos
Posts: 6175
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Local and subroutines inside functions

Post by lexikos » 16 Feb 2018, 22:29

Putting aside the accuracy or inaccuracy of the explanation provided in the documentation and anything else, what the OP is doing is contrary to explicitly documented recommendations:
Such subroutines should use only static and global variables (not locals) if their function is ever called normally.
(I know this was already mentioned. My point is that if you go against this recommendation, you're on your own.)
pneumatic
Posts: 182
Joined: 05 Dec 2016, 01:51

Re: Local and subroutines inside functions

Post by pneumatic » 16 Feb 2018, 23:52

Helgef wrote: an interrupting function call would be able to change the local variables seen by the subroutine.
Except if they're Critical, which many of my subroutines are as they do time-sensitive things such as calculating the number of seconds between now and another time.
lexikos
Posts: 6175
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Local and subroutines inside functions

Post by lexikos » 17 Feb 2018, 00:22

pneumatic wrote:Except if they're Critical, which many of my subroutines are [...]
Perhaps what you mean is that in your script, there would never be an interrupting function call. This is not an exception to Helgef's statement. It is in fact possible for a Critical thread to be interrupted, in which case an interrupting function call could be made, and would see the same local variables that the subroutine sees (unless there was already an instance of that function on the stack, in which case the new thread would get a new instance of the function, so to speak):
A critical thread will be interrupted in emergencies. Emergencies consist of: 1) the OnExit subroutine; 2) any OnMessage() function that monitors a message number less than 0x312 (or a callback triggered by such a message); and 3) any callback indirectly triggered by the critical thread itself (e.g. via SendMessage or DllCall).
pneumatic
Posts: 182
Joined: 05 Dec 2016, 01:51

Re: Local and subroutines inside functions

Post by pneumatic » 17 Feb 2018, 01:08

The same would apply to functions without subroutines inside them, wouldn't it? eg. if a function gets interrupted by another call to itself , its local variables would still be in the previous state (since return was never reached) which could screw up what that function is trying to do. So the function would still need to be made Critical to guarantee consistency of the local vars. Or alternativey, manually blank each var at the beginning of the function. But then that kind of defeats the purpose of using locals.
Helgef
Posts: 3190
Joined: 17 Jul 2016, 01:02
Contact:

Re: Local and subroutines inside functions

Post by Helgef » 17 Feb 2018, 12:14

if a function gets interrupted by another call to itself , its local variables would still be in the previous state
No, recursive function calls are supported (meaning each call has its own set of local variables, but share statics.).

Cheers.
lexikos
Posts: 6175
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Local and subroutines inside functions

Post by lexikos » 17 Feb 2018, 16:06

It seems that I already answered that:
(unless there was already an instance of that function on the stack, in which case the new thread would get a new instance of the function, so to speak)
There would be an instance of the function on the stack only if it was called, not if a label inside the function was called directly.
pneumatic
Posts: 182
Joined: 05 Dec 2016, 01:51

Re: Local and subroutines inside functions

Post by pneumatic » 22 Feb 2018, 05:15

Thanks. Not sure how I missed that bit lexikos :facepalm:
lexikos wrote:This is not an exception to Helgef's statement. It is in fact possible for a Critical thread to be interrupted
Yes, but those are special emergency exceptions to Critical. I was just talking in the general sense that a Critical thread is generally not interrupted, because well, that's kind of the purpose of making them Critical. My script actually does use a callback triggered by DllCall though so I need to be careful if any of those functions contain subroutines, to make sure they aren't using locals.

My project is too big and complex now to move all my subroutines out of functions, but next time what I will do is keep all my subroutines in separate files/sections prefixed by the function they relate to.
Last edited by pneumatic on 22 Feb 2018, 05:22, edited 1 time in total.
Post Reply

Return to “Bug Reports”