New to git: Functions with byref returns

General discussion for topics related to the FreeBASIC project or its community.
Post Reply
fxm
Moderator
Posts: 12159
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: New to git: Functions with byref returns

Post by fxm »

Since fbc version 0.90.0, the BYREF function results are allowed.
So, this can be also applied on operator overloading as CAST in order to return a ZSTRING (because up to now, a ZSTRING could not be returned by value).

But in the following code, the last four lines are allowed only with fbc version 0.91.0:

Code: Select all

Type UDT
  Declare Operator Cast () Byref As Zstring  '' allowed from fbc version 0.90.0
  Dim As Zstring * 10 z = "Hello!"
End Type
Operator UDT.Cast () Byref As Zstring        '' allowed from fbc version 0.90.0
  Return This.z
End Operator

Dim As UDT u
Print *Cast(Zstring Ptr, @u)
Print u + ""
Print u & ""            '' allowed only with fbc version 0.91.0
Print Cast(Zstring, u)  '' allowed only with fbc version 0.91.0
Dim As String s = u     '' allowed only with fbc version 0.91.0
Print u                 '' allowed only with fbc version 0.91.0
About this fix (for fbc version 0.91.0), I did not find any sentence in changelog.txt neither any commit?
Remark:
- 'u + ""' works before version 0.91.0, but not 'u & ""'!
- Even 'Cast(Zstring, u)' does not work before version 0.91.0 while the overload operator CAST is well defined!
dkl
Site Admin
Posts: 3235
Joined: Jul 28, 2005 14:45
Location: Germany

Re: New to git: Functions with byref returns

Post by dkl »

Looks like allowing cast(zstring, ...) was a bug introduced during the 64bit port. I've fixed it for now, though ultimately we may end up allowing zstring to be used as data type on its own (not just with Byref) anyways, for example in order to fix #650. And it makes sense to allow it when dealing with Byref things anyways.

Code: Select all

Dim As String s = u
Print u
This works since 00d8aa37 (found via git bisect), and while it surely wasn't directly intended, it's an indirect side-effect and makes sense to me. There will be an implicit dereference done for the Byref As Zstring function result, and thanks to the overload resolution fixes related to that, the Byref As Zstring function result can now be used with Print or string assignments.
fxm
Moderator
Posts: 12159
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: New to git: Functions with byref returns

Post by fxm »

Thank you dkl.

I discovered this behavior when coding a "smart zstring" (with automatic sizing of memory and automatic freeing of memory) using fbc 0.91.0, then by chance when compiling it with fbc 0.90.1!

Code: Select all

Type smartZstring
  Public:
    Declare Constructor ()
    Declare Operator Let (Byval pz As Zstring Ptr)
    Declare Operator Let (Byref sz As smartZstring)
    Declare Operator Cast () Byref As Zstring
    Declare Operator @ () As Zstring Ptr
    #if __fb_version__ >= "0.91.0"
    Declare Operator [] (Byval index As Uinteger) Byref As Ubyte
    #endif
    Declare Constructor (Byval pz As Zstring Ptr)
    Declare Constructor (Byref sz As smartZstring)
    Declare Destructor ()
  Private:
    Dim As Zstring PTR psz
End Type

Constructor smartZstring ()
End Constructor

Operator smartZstring.Let (Byval pz As Zstring Ptr)
  Deallocate(This.psz)
  This.psz = Callocate(Len(*pz)+1, Sizeof(Zstring))
  *This.psz = *pz
End Operator

Operator smartZstring.Let (Byref sz As smartZstring)
  If @This <> @sz Then
    Deallocate(This.psz)
'    #if __fb_version__ >= "0.91.0"
'    This.psz = Callocate(Len(Cast(Zstring, sz))+1, Sizeof(Zstring))
'    #else
    This.psz = Callocate(Len(*Cast(Zstring Ptr, @sz))+1, Sizeof(Zstring))
'    #endif
    *This.psz = sZ
  End If
End Operator

Operator smartZstring.Cast () Byref As Zstring
  Return *This.psz
End Operator

Operator smartZstring.@ () As Zstring Ptr
  Return This.psz
End Operator

#if __fb_version__ >= "0.91.0"
Operator smartZstring.[] (Byval index As Uinteger) Byref As Ubyte
  Return (*This.psz)[index]
End Operator
#endif

Constructor smartZstring (Byval pz As Zstring Ptr)
  This = pz
End Constructor

Constructor smartZstring (Byref sz As smartZstring)
  This = sz
End Constructor

Destructor smartZstring ()
  Deallocate(This.psz)
End destructor

#if __fb_version__ < "0.91.0"
Operator & (Byval pz As Zstring Ptr, Byref sz As smartZstring) As String
  Return *pz + sz
End Operator

Operator & (Byref sz As smartZstring, Byval pz As Zstring Ptr) As String
  Return sz + *pz
End Operator

Operator & (Byref sz1 As smartZstring, Byref sz2 As smartZstring) As String
  Return sz1 + sz2
End Operator
#endif

'-------------------------------------------------------------------------------------------------------

Scope
  Dim As smartZstring sz1 = "FreeBASIC"
  #if __fb_version__ >= "0.91.0"
  Print sz1
  #else
  Print "" & sz1
  #endif
  Print "'" & sz1 & "'"
  sz1 &= " version 0.91.0"
  Print "'" & sz1 & "'"
  #if __fb_version__ >= "0.91.0"
  Print sz1[9]
  sz1[9] = Asc("/")
  Print "'" & sz1 & "'"
  Print "'" & (@sz1)[10] & "'"
  #endif
  Dim As smartZstring sz2
  sz2 = sz1
  sz1 = ""
  Print "'" & sz2 & "'"
  Print Len(sz2)
  Print Len(*@sz2)
  Dim As smartZstring sz3 = sz2
  Print "'" & sz3 & "'"
End Scope

Sleep
fxm
Moderator
Posts: 12159
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: New to git: Functions with byref returns

Post by fxm »

Referring to my code above:
- Modifying inside the Let operator body, because at present time, 'Cast(Zstring, sz)' is again disallowed while the overload operator CAST is well defined for the 'sz' object!
- I don't understand why this has been forbidden again?
dkl
Site Admin
Posts: 3235
Joined: Jul 28, 2005 14:45
Location: Germany

Re: New to git: Functions with byref returns

Post by dkl »

it wasn't allowed intentionally, but only as a side-effect of a bug, which I have fixed for now.
fxm
Moderator
Posts: 12159
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: New to git: Functions with byref returns

Post by fxm »

A function can now return by reference a string passed by value without compiler error as also for real complex objects!
Version 0.91.0:
[added]
- BYVAL AS STRING is now working properly: it now has BYVAL semantics and no longer behaves like BYREF AS ZSTRING. Modifications made by the callee are not visible to the caller, as for other BYVAL parameters. (BYVAL AS STRING is implemented by copying the string argument into a temporary STRING, whose descriptor is then passed BYREF to the procedure)
Now a string can be passed as value as defined above.
So the temporary object is not local to procedure but temporary to the caller.
Therefore there is no compiler error when passing a string by value to a function returning it by reference:

Code: Select all

Function fs (Byval s As String) Byref As String
  Return s
End Function

Dim As String s = "freeBASIC"
fs(s) => fs(s) & " version 0.91.0"
Print s
This behavior for a string is different from other predefined datatypes as the numeric types.
It is the same as for a "complex" object (object with copy-constructor or destructor at least).

This can be a bit confusing for non-specialists!
Fortunately the passing convention for string parameter remains ByRef by default.
(when will also the passing arrays by value!)
fxm
Moderator
Posts: 12159
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: New to git: Functions with byref returns

Post by fxm »

Weird behavior (or bug) when a STRING is passed BYVAL to a function returning BYREF

Complex objects (for example with destructors) are always passed BYREF. In case of BYVAL they will be copied to a temporary object, but that is then still passed BYREF implicitly, so that the destructor can be called from within the caller's context, after the callee returns.

The following example shows that:
- (1): the temporary object (reference) can be use safely in an expression's code that includes the call,
- (2): after call expression, the temporary object (reference) is no more usable because destructed after expression evaluation,
- (3): the original object is not modified.

Code: Select all

Type UDT
  Dim As String s = "freeBASIC"
End Type

Function f (Byval u As UDT) Byref As UDT
  u.s &= " version 0.91.0"
  Return u
End Function

Dim As UDT u

Print "(1):"
Print "'" & f(u).s & "'"
Print "'" & (@(f(u)))->s & "'"
Print: Print "(2):"
Dim As UDT Ptr p = @(f(u))
Print "'" & p->s & "'"
Print: Print "(3):"
Print "'" & u.s & "'"

Sleep

Code: Select all

(1):
'freeBASIC version 0.91.0'
'freeBASIC version 0.91.0'

(2):
''

(3):
'freeBASIC'
For the STRING that is a pseudo object with destructor, the functioning should be the same:
- BYVAL AS STRING is now working properly: it now has BYVAL semantics and no longer behaves like BYREF AS ZSTRING. Modifications made by the callee are not visible to the caller, as for other BYVAL parameters. (BYVAL AS STRING is implemented by copying the string argument into a temporary STRING, whose descriptor is then passed BYREF to the procedure).

But the following example shows that the temporary string (reference) is never usable, even inside the call expression:

Code: Select all

Function f (Byval s As String) Byref As String
  s &= " version 0.91.0"
  Return s
End Function

Dim As String s = "freeBASIC"

Print "(1):"
Print "'" & f(s) & "'"
Print "'" & *(@(f(s))) & "'"
Print: Print "(2):"
Dim As String Ptr p = @(f(s))
Print "'" & *p & "'"
Print: Print "(3):"
Print "'" & s & "'"

Sleep

Code: Select all

(1):
''
''

(2):
''

(3):
'freeBASIC'
Even the simplest 'Print f(s)' fails!
dkl
Site Admin
Posts: 3235
Joined: Jul 28, 2005 14:45
Location: Germany

Re: New to git: Functions with byref returns

Post by dkl »

This bug should be fixed now (afcced48) - the compiler was freeing the temporary strings too early: directly after the call, instead of at the end of the statement, which is a difference (only) if there's more than just that call in that statement.

This also led me to discover a bug in Swap (55db9794) and a more fundamental issue with ThreadCall (can't really pass temporary objects to the thread byref, because they will be freed at the end of the ThreadCall statement - there's a race-condition there), that I'm not exactly sure how it can be fixed yet.
fxm
Moderator
Posts: 12159
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: New to git: Functions with byref returns

Post by fxm »

fxm wrote:Weird behavior (or bug) when a STRING is passed BYVAL to a function returning BYREF

Complex objects (for example with destructors) are always passed BYREF. In case of BYVAL they will be copied to a temporary object, but that is then still passed BYREF implicitly, so that the destructor can be called from within the caller's context, after the callee returns.
.....
For the STRING that is a pseudo object with destructor, the functioning should be the same:
- BYVAL AS STRING is now working properly: it now has BYVAL semantics and no longer behaves like BYREF AS ZSTRING. Modifications made by the callee are not visible to the caller, as for other BYVAL parameters. (BYVAL AS STRING is implemented by copying the string argument into a temporary STRING, whose descriptor is then passed BYREF to the procedure).

But the following example shows that the temporary string (reference) is never usable, even inside the call expression:

Code: Select all

Function f (Byval s As String) Byref As String
  s &= " version 0.91.0"
  Return s
End Function

Dim As String s = "freeBASIC"

Print "(1):"
Print "'" & f(s) & "'"
Print "'" & *(@(f(s))) & "'"
Print: Print "(2):"
Dim As String Ptr p = @(f(s))
Print "'" & *p & "'"
Print: Print "(3):"
Print "'" & s & "'"

Sleep

Code: Select all

(1):
''
''

(2):
''

(3):
'freeBASIC'
Even the simplest 'Print f(s)' fails!
dkl wrote:This bug should be fixed now (afcced48) - the compiler was freeing the temporary strings too early: directly after the call, instead of at the end of the statement, which is a difference (only) if there's more than just that call in that statement.
Thanks dkl, now this works well:

Code: Select all

(1):
'freeBASIC version 0.91.0'
'freeBASIC version 0.91.0'

(2):
''

(3):
'freeBASIC'
fxm
Moderator
Posts: 12159
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: New to git: Functions with byref returns

Post by fxm »

dkl wrote:This bug should be fixed now (afcced48) - the compiler was freeing the temporary strings too early: directly after the call, instead of at the end of the statement, which is a difference (only) if there's more than just that call in that statement.
In the following two configurations where the passed parameter (by value or by reference) is optional, it is also verified that omit the parameter in the call does not affect the validity of the returned (by reference) local variable which is usable inside the call expression (destroyed only at the end of the call statement):

Code: Select all

Function f (Byval s As String = "") Byref As String
  s &= " version 0.91.0"
  Return s
End Function

Dim As String s = "freeBASIC"

Print "(1):"
Print "'" & f() & "'"
Print "'" & *(@(f())) & "'"
Print: Print "(2):"
Dim As String Ptr p = @(f())
Print "'" & *p & "'"
Print: Print "(3):"
Print "'" & s & "'"

Sleep

Code: Select all

(1):
' version 0.91.0'
' version 0.91.0'

(2):
''

(3):
'freeBASIC'

Code: Select all

Function f (Byref s As String = "") Byref As String
  s &= " version 0.91.0"
  Return s
End Function

Dim As String s = "freeBASIC"

Print "(1):"
Print "'" & f() & "'"
Print "'" & *(@(f())) & "'"
Print: Print "(2):"
Dim As String Ptr p = @(f())
Print "'" & *p & "'"
Print: Print "(3):"
Print "'" & s & "'"

Sleep

Code: Select all

(1):
' version 0.91.0'
' version 0.91.0'

(2):
''

(3):
'freeBASIC'
fxm
Moderator
Posts: 12159
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: New to git: Functions with byref returns

Post by fxm »

When using an expression with function result returned by reference:
- On the left-hand side of an assignment expression using the '=' symbol, the result of the function (returned by reference) must be enclosed in parentheses when the function calls one single argument, in order to solve the parsing ambiguity.
- From fbc version 0.90, '=>' can be used for assignments, in place of '=', same as for initializers, allowing to avoid parsing ambiguity (without parentheses).

For all combined assignment operators (as '+='), use the assignment operator '=>' (as '+=>') also works but does not allow to omit the parentheses wrapping the function result returned by reference!

Example:

Code: Select all

Function f (Byref i As Integer) Byref As Integer
  Function = i
End function

Dim As Integer I
'f(I) = 1  '' warning 34(0): '=' parsed as equality operator in function argument,
           '' not assignment to BYREF function result
(f(I)) = 1
Print I
f(I) => 2
Print I

'f(I) += 1 '' error 9: Expected expression
(f(I)) += 1
Print I
'f(I) +=> 1  '' error 9: Expected expression
(f(I)) +=> 1
Print I

Sleep

Code: Select all

 1
 2
 3
 4
Could this parser behavior be improved for all combined assignment operators using the assignment operator '=>', when the wrapping parentheses are missing?
f(I) +=> 1 '' error 9: Expected expression[/tt]
Last edited by fxm on Sep 20, 2014 15:43, edited 1 time in total.
fxm
Moderator
Posts: 12159
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: New to git: Functions with byref returns

Post by fxm »

fxm wrote:Could this parser behavior be improved for all combined assignment operators using the assignment operator '=>', when the wrapping parentheses are missing?
f(I) +=> 1 '' error 9: Expected expression[/tt]
No echo from admin on my above questionning!

If you have courage (and time), can you also answer to my other questionnings:
- http://www.freebasic.net/forum/viewtopi ... 84#p200284
- http://www.freebasic.net/forum/viewtopi ... 37#p200337
- http://www.freebasic.net/forum/viewtopi ... 99#p200399
dkl
Site Admin
Posts: 3235
Joined: Jul 28, 2005 14:45
Location: Germany

Re: New to git: Functions with byref returns

Post by dkl »

fxm wrote:Could this parser behavior be improved for all combined assignment operators using the assignment operator '=>', when the wrapping parentheses are missing?
Done in Git now. It works with = now too, not just =>. There was no need to focus on => for this, since += is not ambigious anyways. (Only = alone is ambigious)
fxm
Moderator
Posts: 12159
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: New to git: Functions with byref returns

Post by fxm »

Thanks dkl for:
parser: Check for self-assignments when parsing BOPs for disambiguation.

Now, the combined assignment operators (except pure assignment operator) can be used on the byref returns of functions of one single argument, and this without parentheses for wrapping the function expressions:

Code: Select all

Function f (Byref i As Integer) Byref As Integer
  Function = i
End function

Dim As Integer I
'f(I) = 1  '' warning 34(0): '=' parsed as equality operator in function argument,
           ''   not assignment to BYREF function result
(f(I)) = 1
Print I
f(I) => 2
Print I

f(I) += 1
Print I
f(I) +=> 1
Print I

Sleep

Code: Select all

 1
 2
 3
 4
Last edited by fxm on Oct 26, 2020 17:02, edited 1 time in total.
fxm
Moderator
Posts: 12159
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: New to git: Functions with byref returns

Post by fxm »

I still do not understand why 'Cast(Zstring, u)' is prohibited in the below example, when 'u' is an instance of an UDT where the corresponding member operator 'Cast() Byref As Zstring' is defined:

Code: Select all

Type UDT
  Declare Operator Cast () Byref As Zstring
  Dim As Zstring * 7 z = "Hello0"
End Type
Operator UDT.Cast () Byref As Zstring
  Return This.z
End Operator

Dim As UDT u
Print u
*Cast(Zstring Ptr, @u.z) = "Hello1"
Print *Cast(Zstring Ptr, @u.z)
'Cast(Zstring, u) = "Hello2"  '' error 28: Expected pointer
'Print Cast(Zstring, u)       '' error 28: Expected pointer
At opposite, this seems to be authorized for all other data types, such as 'String' for example ('Cast(String, u)' autorized if corresponding member operator 'Cast() Byref As String' is defined):

Code: Select all

Type UDT
  Declare Operator Cast () Byref As String
  Dim As String s = "Hello0"
End Type
Operator UDT.Cast () Byref As String
  Return This.s
End Operator

Dim As UDT u
Print u
*Cast(String Ptr, @u.s) = "Hello1"
Print *Cast(String Ptr, @u.s)
Cast(String, u) = "Hello2"
Print Cast(String, u)
Post Reply