BUG - UDT does not work correctly with a dynamic 'String * N' array as field

General FreeBASIC programming questions.
Post Reply
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

BUG - UDT does not work correctly with a dynamic 'String * N' array as field

Post by fxm »

BUG:
A UDT does not work correctly with a dynamic 'String * N' array as field.
This works with a dynamic 'Zstring * N' array as field.
This obviously works with a static 'String * N' array as field.

Precise bug
When called, the implicit let-operator (and the implicit copy-constructor) of the UDT crashes.
No more problem when an explicit let-operator is properly defined by the user (with this explicit let-operator, the copy-construction works too).
I think this bug dates back to support for dynamic arrays in UDTs (fbc 1.00.0).

Examples
- Example with the implicit let-operator => crashing :

Code: Select all

Type UDT
    Dim As String * 1 array(Any)
End Type

Dim As UDT u1
Redim u1.array(3)
u1.array(0) = "a"
u1.array(1) = "b"
u1.array(2) = "c"
u1.array(3) = "d"

For I As Integer = Lbound(u1.array) To Ubound(u1.array)
    Print "'" & u1.array(I) & "'",
Next I
Print

Dim As UDT u2
u2 = u1

For I As Integer = Lbound(u2.array) To Ubound(u2.array)
    Print "'" & u2.array(I) & "'",
Next I
Print

Sleep
- Example with an explicit user-defined let-operator => working :

Code: Select all

Type UDT
    Dim As String * 1 array(Any)
    Declare Operator Let(Byref u As UDT)
End Type

Operator UDT.Let(Byref u As UDT)
    If Ubound(u.array) >= Lbound(u.array) Then
        Redim This.array(Lbound(u.array) To Ubound(u.array))
        For I As Integer = Lbound(u.array) To Ubound(u.array)
            This.array(I) = u.array(I)
        Next I
    Else
        Erase This.array
    End If
End operator

Dim As UDT u1
Redim u1.array(3)
u1.array(0) = "a"
u1.array(1) = "b"
u1.array(2) = "c"
u1.array(3) = "d"

For I As Integer = Lbound(u1.array) To Ubound(u1.array)
    Print "'" & u1.array(I) & "'",
Next I
Print

Dim As UDT u2
u2 = u1

For I As Integer = Lbound(u2.array) To Ubound(u2.array)
    Print "'" & u2.array(I) & "'",
Next I
Print

Sleep
coderJeff
Site Admin
Posts: 4326
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: BUG - UDT does not work correctly with a dynamic 'String * N' array as field

Post by coderJeff »

I see a couple of possibilities for a fix:
1) add a special case for STRING*N in the generation of the implicit let operator
2) review the internal handling of STRING*N expectations.

STRING*N is like a STRING except:
- STRING has a descriptor, STRING*N does not
- STRING can be resized, STRING*N is a fixed length.

Internally, sometimes STRING*N is treated like a STRING and sometimes treated like a *(ZSTRING PTR), depending on context.
fxm wrote: Jan 07, 2023 16:46 Precise bug
When called, the implicit let-operator (and the implicit copy-constructor) of the UDT crashes.
No more problem when an explicit let-operator is properly defined by the user (with this explicit let-operator, the copy-construction works too).
I think this bug dates back to support for dynamic arrays in UDTs (fbc 1.00.0).
The problem maybe even goes back further with the special handling of STRING*N and is maybe left over from early versions of the compiler. The dynamic array copy algorithm looks mostly correct to me in that the intent is to generically loop through all elements and perform an assignment. So it should work for any array type, either performing shallow copies or calling let operators for types.

The array copy looks like this when expanded - using dereferenced pointers for assignment :

Code: Select all

'' equivalent code for zsstring*N array copy

dim src(0 to 3) as zstring * 2 = {"A","B","C","D"}
dim dst(0 to 3) as zstring * 2

var psrc = @src(0)
var pdst = @dst(0)
var pend = @src(0) + sizeof(zstring*2) * (ubound(src) - lbound(src) + 1)

while( psrc < pend )
	*pdst = *psrc
	pdst += sizeof(typeof(dst(0)))
	psrc += sizeof(typeof(src(0)))
wend

for i as integer = lbound(src) to ubound(src)
	print src(i), dst(i)
next
sleep
The interesting part is that pointer math is not allowed on STRING*N PTR. So the dynamic array copy code generation algorithm fails to produce a working loop in the let operation:

Code: Select all

dim z as zstring * 10
var pz = @z
print pz
pz += 1  '' allowed
print pz

dim s as string * 10
var ps = @s
print ps 
ps += 1  '' error
print ps

sleep
For some reason, the logic in fbc is to always calculate a length of zero for STRING*N in certain operations.
Two consequences of this is that:
1) STRING*N must always be handled in a special way by the compiler, otherwise
2) certain expressions fail, which may be expected, I don't know without reviewing all the instances of this calculation

However, for follow-up it would be useful to also know if there are other failures possible. For example, the type is a UDT and does some overloading that would affect `*dst = *src` assignments or affect '*dst += 1' operations could also somehow cause the dynamic array copy to fail.
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: BUG - UDT does not work correctly with a dynamic 'String * N' array as field

Post by fxm »

I just filled in a short bug report referring to the posts here:
#977 UDT does not correctly support dynamic 'String * N' arrays as fields
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: BUG - UDT does not work correctly with a dynamic 'String * N' array as field

Post by fxm »

coderJeff wrote: Jan 08, 2023 14:28 Internally, sometimes STRING*N is treated like a STRING and sometimes treated like a *(ZSTRING PTR), depending on context.
coderJeff wrote: Jan 08, 2023 14:28 The array copy looks like this when expanded - using dereferenced pointers for assignment :

Code: Select all

'' equivalent code for zsstring*N array copy

dim src(0 to 3) as zstring * 2 = {"A","B","C","D"}
dim dst(0 to 3) as zstring * 2

var psrc = @src(0)
var pdst = @dst(0)
var pend = @src(0) + sizeof(zstring*2) * (ubound(src) - lbound(src) + 1)

while( psrc < pend )
	*pdst = *psrc
	pdst += sizeof(typeof(dst(0)))
	psrc += sizeof(typeof(src(0)))
wend

for i as integer = lbound(src) to ubound(src)
	print src(i), dst(i)
next
sleep

3 lines changed and it seems to work for a 'String * N' array:

Code: Select all

dim src(0 to 3) as string * 2 = {"A1","B2","C3","D4"}
dim dst(0 to 3) as string * 2

dim as zstring ptr psrc = cptr(zstring ptr, @src(0))
dim as zstring ptr pdst = cptr(zstring ptr, @dst(0))
var pend = psrc + sizeof(string*2) * (ubound(src) - lbound(src) + 1)

while( psrc < pend )
	*pdst = *psrc
	pdst += sizeof(typeof(dst(0)))
	psrc += sizeof(typeof(src(0)))
wend

for i as integer = lbound(src) to ubound(src)
	print src(i), dst(i)
next
sleep
coderJeff
Site Admin
Posts: 4326
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: BUG - UDT does not work correctly with a dynamic 'String * N' array as field

Post by coderJeff »

I guess cast zstring would be ok. It looks like STRING*N won't copy past null characters. Also, I see a regression or a pre-existing bug with initializers ... darn.

Code: Select all

#macro dumpstr( x )
	print #x; ": ";
	for i as integer = 0 to sizeof(x)-1
		print x[i]; " "; 
	next
	print 
#endmacro

dim s as string * 10 = "ABC" + chr(0) + "DEF"
dumpstr( s )

dim d1 as string * 10 = s  '' bad initializer
dumpstr( d1 )

dim d2 as string * 10
d2 = s
dumpstr( d2 )

/' 
OUTPUT with STRING * 10
s: 65 66 67 0 68 69 70 0 0 0 0
d1: 65 66 67 0 0 0 0 0 0 0 64
d2: 65 66 67 0 0 0 0 0 0 0 0

OUTPUT with ZSTRING * 10
s: 65 66 67 0 68 69 70 0 25 0
d1: 65 66 67 0 200 254 24 0 59 21
d2: 65 66 67 0 0 0 0 0 0 0
'/

sleep
Post Reply