Modify

Opened 10 years ago

Closed 10 years ago

Last modified 8 years ago

#2713 closed Bug (Duplicate)

ByRef Copy Bug

Reported by: anonymous Owned by:
Milestone: Component: AutoIt
Version: 3.3.11.5 Severity: None
Keywords: byRef Cc:

Description (last modified by Mat)

In the following script, the presence of ByRef causes $b to be a reference, not a copy.

Local $a[2] = [1, 2]

test($a[0])

Func test(ByRef $foo)
    Local $b = $a
    $foo += 1

    ConsoleWrite($a[0] & @LF)
    ConsoleWrite($b[0] & @LF)
EndFunc   ;==>test

Attachments (0)

Change History (11)

comment:1 follow-up: Changed 10 years ago by Mat

  • Description modified (diff)

This has been discussed here: http://www.autoitscript.com/forum/topic/161296-supposed-byref-bug/ (Link is to MVP section)

I've changed the code in the report to be much simpler, without the OPs attempt to write object oriented AutoIt without objects.

comment:2 in reply to: ↑ 1 Changed 10 years ago by anonymous

Replying to Mat:

This has been discussed

Was there an outcome of the discussion? Is it a language feature?

comment:3 follow-up: Changed 10 years ago by Mat

The discussion was only among the MVPs, the developers have not yet commented. The majority of the conversation was about what actually caused the behaviour you were seeing.

If by "language feature" you are asking whether you should rely on this behaviour in code, you definitely shouldn't. There are better ways to write your original code that avoid this issue.

As to whether it's a bug, I don't know. I'd be inclined to say yes, other MVPs thought it wasn't though, so we'll leave it to Jon to decide.

comment:4 in reply to: ↑ 3 ; follow-up: Changed 10 years ago by anonymous

Replying to Mat:

The discussion was only among the MVPs, the developers have not yet commented. The majority of the conversation was about what actually caused the behaviour you were seeing.

Is this connected to ticket 2502?

If by "language feature" you are asking whether you should rely on this behaviour in code, you definitely shouldn't. There are better ways to write your original code that avoid this issue.

I may not even know that the argument is an element of the copied array (or object).

comment:5 in reply to: ↑ 4 ; follow-up: Changed 10 years ago by Mat

  • Resolution set to Duplicate
  • Status changed from new to closed

Replying to anonymous:

Is this connected to ticket 2502?

Yes it is. I hadn't realised this had been looked at before (but it looks like the conclusion was similar (variable != r-value, and byref might use either, or might not). I'll close this as a duplicate.

If you want to guard against this, then just force it to copy by writing to the array:

Local $a[2] = [1, 2]

test($a[0])

Func test(ByRef $foo)
    Local $b = $a
    $b[0] = $b[0] ; Force COW

    $foo += 1

    ConsoleWrite($a[0] & @LF)
    ConsoleWrite($b[0] & @LF)
EndFunc   ;==>test

comment:6 in reply to: ↑ 5 Changed 10 years ago by anonymous

Replying to Mat:

If you want to guard against this, then just force it to copy by writing to the array

That's an ugly hack. Should I do this every time I copy a variable that might be an array, if the function has a ByRef argument (like $instance, the class generator creates countless arguments)?

comment:7 follow-up: Changed 10 years ago by Melba23

In my testing the problem only arises when you pass a single element of an array ByRef - passing the entire array produces what would be expected, regardless of whether the copy array is taken from the original or the passed array:

Global $a[2] = [1, 2]

test1($a)
test2($a[0])
test3($a)

; Entire array passed
Func test1(ByRef $foo)
	Local $b = $a ; $a is Global
	$foo[0] += 1
	ConsoleWrite("Should be 2 : " & $a[0] & @CRLF)
	ConsoleWrite("Should be 1 : " & $b[0] & @CRLF)
	ConsoleWrite(@CRLF)
EndFunc   ;==>test1

; Single array element passed
Func test2(ByRef $foo)
	Local $b = $a
	$foo += 1
	ConsoleWrite("Should be 3 : " & $a[0] & @CRLF)
	ConsoleWrite("Should be 1 : " & $b[0] & @CRLF) ; Error here
	ConsoleWrite(@CRLF)
EndFunc   ;==>test2

; Entire array passed
Func test3(ByRef $foo)
	Local $b = $foo ; $foo is $a passed ByRef
	$foo[0] += 1
	ConsoleWrite("Should be 4 : " & $a[0] & @CRLF)
	ConsoleWrite("Should be 3 : " & $b[0] & @CRLF)
EndFunc   ;==>test3

The comment:

Should I do this every time I copy a variable that might be an array

is nonsense as passing an array does not produce the error. You can only pass an array element by using the [ # ] syntax, so you have an easy check on whether the problem will appear and can take measures to deal with it.

Better still - pass the entire array rather then a single element and then the problem will never arise.

M23

comment:8 in reply to: ↑ 7 Changed 10 years ago by anonymous

Replying to Melba23:

The comment:

Should I do this every time I copy a variable that might be an array

is nonsense as passing an array does not produce the error.
[...]
Better still - pass the entire array rather then a single element and then the problem will never arise.

It doesn't matter that the ByRef argument might be an array. Also, the following does exhibit the problem:

Local $instance = [1]
Local $array = [$instance, 2]

test ($array [0], $array)

Func test (ByRef $first, $arr)
	Local $copy = $arr
	$first [0] = 3
	; "3 3", should be "1 3"
	ConsoleWrite (($copy [0]) [0] & " " & ($arr [0]) [0] & @CRLF)
EndFunc


You can only pass an array element by using the [ # ] syntax, so you have an easy check on whether the problem will appear and can take measures to deal with it.

I don't know if the function will be called with a reference to a "normal" variable or an "unnatural" array element. It's required that the argument is a reference, I need to modify it. The function also copies another argument (which may be an array). There are some possible solutions:

  1. Duplicate the function and every function that forwards these two arguments: One version for "normal" variables and one version for "unnatural" array elements. If the other argument is an array, the second version forces AutoIt to copy the array, if the other argument really is an array. Then I will just need to call the adjusted function, if I use an "unnatural" array element.
  2. Like the first with additional arguments indicating the kind of referenced object.
  3. I could wrap the other argument to that function (and any other function like that) with a function like "identity" that simply returns the passed value when using an "unnatural" array element.
  4. I could ensure that any copy of the other argument is a real copy, if that argument is an array. Again, I might use "identity".

The first three are inacceptable, they would require the caller to do something which should be done automatically and reliably (by the interpreter), without needing to examine every single array access. The last two actually copy the other argument if it is an array, even when the copied array never contains the referenced object.

comment:9 Changed 10 years ago by Melba23

I am sure it is possible to come up with a myriad scenarios where the interpreter does not do what someone thinks it should - just remember this is a free-to-use hobbyist language and you cannot expect perfection. Anyway, I am not getting into discussions in Trac - there is an open ticket which may, or may not, get fixed (it would not be high on my list for the next round of development work). Now you realise that the problem exists, I suggest you recast your code to get around it. :)

M23

comment:10 Changed 10 years ago by mLipok

Personally, I'm glad I know that.
It is always better to know.
Thanks for the info.

comment:11 Changed 8 years ago by not so anonymous

	ConsoleWrite("Should be 1 : " & $b[0] & @CRLF) ; Error here

Yep. Someone forgot, while writing that, that test1($a) has actually changes $a[0] from a 1 to a 2.

O wait, this is not the ByRef issue I'm looking for.

Guidelines for posting comments:

  • You cannot re-open a ticket but you may still leave a comment if you have additional information to add.
  • In-depth discussions should take place on the forum.

For more information see the full version of the ticket guidelines here.

Add Comment

Modify Ticket

Action
as closed The ticket will remain with no owner.
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.