FreeBASIC 1.10.1 Release Discussion

General discussion for topics related to the FreeBASIC project or its community.
Post Reply
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: FreeBASIC 1.10.0 Development

Post by coderJeff »

Looks like I broke something on the latest commits. I think we fixed the case where initializers are up-casted and assigned directly to a parent, but then broke the case where initializers are assigned to a temporary variable and then up-casted to the parent.

I've been so concerned about what is happening at the top level dim as parent p = .... that I kind of forgot that initializers are actually a tree structure that can contain other initializers.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC 1.10.0 Development

Post by fxm »

Yes.
I have noticed this bad behavior (I was waiting a bit to report):

Code: Select all

Type Parent
    Dim As Integer I
End Type

Type Child Extends Parent
    Dim As Integer J
End Type

Dim As Child c : c.I = 1 : c.J = 2

Dim As Parent p1 = Type<Child>(c)
Print p1.I           '' should be '1'

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

Re: FreeBASIC 1.10.0 Development

Post by coderJeff »

We are trying to fix the very bad case where initializer is up-casted and overwrites memory that shouldn't be assigned. Turns out is rather tricky to solve.

For anyone curious, here is what is happening on the development side as I muddle my way through this:
- we can't ask the parser to figure it out, because we need to completely parse the initializer first to figure out what to do next
- we can't ask assignment in AST to figure it out, because it just builds assignments using a base memory address
- so we need to make the decision in between using common code from 2 cases
- case 1: direct assignments, and up-casting
- case 2: temporary assignment, then up-casting
- there might be other cases like when we involve initializer values that are result of constructor or function calls ... but the point is that there are at least 2 kinds of code generation, and fixing one has broken the other.

Simple example:

Code: Select all

type parent
	i as integer
end type
type child extends parent
	j as integer
end type
dim as child c
In case 1, initializers are assigned directly (and is what we tried to fix in last commits):

Code: Select all

dim as parent p0 = type<child>(1,2)

	'' pseudo code generation
	*(cast( integer ptr, @p0 ) + 0) = 1
	*(cast( integer ptr, @p0 ) + 1) = 2   '' don't allow because target is too small
In this next case, an instance of child is constructed to a temporary variable first, and I incorrectly discarded the assignment of child initializer to temporary child variable:

Code: Select all

dim as parent p1 = type<child>(c)

	'' pseudo code generation
	var tmp = c                          '' oops, discarded because sizeof(c) > sizeof(p) -- BROKEN
	memcpy( @p1, @tmp, sizeof(p1) )       '' handled by AST assignment
I don't know if it helps to share this, but this is kind of the process I go through to try and determine where to make changes in the compiler.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC 1.10.0 Development

Post by fxm »

But this seems to work:

Code: Select all

Dim As Parent p1 = Cast(Parent, Type<Child>(c))
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: FreeBASIC 1.10.0 Development

Post by coderJeff »

fxm wrote: Dec 26, 2022 18:45 But this seems to work:

Code: Select all

Dim As Parent p1 = Cast(Parent, Type<Child>(c))
For the user, this probably looks the same since it is only the difference between an implicit cast and explicit cast.

In this case:
- a temporary variable is instanced by type<child>(c)
- explict cast( parent, tmp ) tells the compiler to see tmp as the parent type
- memcpy( @p1, @tmp, sizeof( parent ) )

The initializer tree stored internally in the compiler is different:
Cast(Parent, Type<Child>(c)) --> contains an explicit cast in the initializer
Type<Child>(c) --> does not contain an explicit cast

We want the same result, but the compiler takes a different code path through the parser to get there.

EDIT:
I think I have an improvement. Just trying to write a few tests to capture what we have already covered.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC 1.10.0 Development

Post by fxm »

Well done Jeff.
After some testing, I do not see any other bugs (including testing with 'Base()'), except for 'New()' which is not yet allowed with a derived initializer ('Dim As Parent Ptr pp = New Parent(child_instance)').
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: FreeBASIC 1.10.0 Development

Post by coderJeff »

Thanks, fxm.
fxm wrote: Dec 27, 2022 13:16 After some testing, I do not see any other bugs (including testing with 'Base()'), except for 'New()' which is not yet allowed with a derived initializer ('Dim As Parent Ptr pp = New Parent(child_instance)').
Yes, NEW() still needs work.

I think BASE() is correct, and my thoughts were that we would need something similiar with NEW().

Maybe this will help explain when I am trying to indicate that up-casting is disallowed:

Code: Select all

type T1
	i as integer
end type
type T2 extends T1
	j as integer
end type
type T3 extends T2
	k as integer
	declare constructor()
end type

dim shared iT2 as T2 = (1, 2)

constructor T3
	base( iT2 )

	'' with up-casting disallowed in base initializer then we get expected
	'' i=1   j=2   k=0

	'' and
	'' base( iT2, 3 ) --> error

	'' but if up-casting from T2 to T1 is allowed then we get: 
	'' i=1   j=0   k=0

	'' and also allowed would be:
	'' base( iT2, 3 )
	'' i=1   j=3   k=0 

end constructor

dim x as T3
print x.i, x.j, x.k
sleep
I feel like we should have something similar with NEW() but it doesn't work right yet.


EDIT:
Ah, recursion gives me headaches again. This isn't quite precise enough to show internals, but something like...

Code: Select all

Dim As Parent Ptr pp = New Parent(child_instance)
                                  ^^^^^^^^^^^^^^   Initializer to Parent
                       ^^^^^^^^^^^^^^^^^^^^^^^^^   Initializer to Parent Ptr
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: FreeBASIC 1.10.0 Development

Post by coderJeff »

Well, I've been fighting with this all day and need a break from it. I will revert the changes for NEW() only so it should work like before. I don't think it is correct, but I don't know how to solve it right now.

With the changes for NEW() reverted:

Code: Select all

type T1
	i as integer
end type
type T2 extends T1
	j as integer
end type
type T3 extends T2
	k as integer
end type

dim x as T2 = (6,7)

scope
	'' this looks correct to me
	dim as T3 y = type<T3>(x)
	print y.i, y.j, y.k           
	'' 6, 7, 0
end scope

scope
	'' need to allow up-casting for this to work ...
	dim as T1 ptr y = new T1(x) 
	print y->i                    
	'' 6
	delete y
end scope

scope
	'' but then this looks wrong, since x is up-casted to initialize T1
	dim as T3 ptr y = new T3(x)   
	'' 6, 0, 0 
	print y->i, y->j, y->k
	delete y
end scope

scope
	'' type() doesn't allow upcasting, so I guess this right, but clumbsy 
	dim as T3 ptr y = new T3(type<T3>(x))   
	'' 6, 7, 0 
	print y->i, y->j, y->k
	delete y
end scope

scope
	'' explict cast to T2 does nothing due the implicit up-casting to T1
	dim as T3 ptr y = new T3(cast(T2, x))   
	'' 6, 7, 0 
	print y->i, y->j, y->k
	delete y
end scope

scope
	'' and this looks wrong, due the implicit up-casting
	dim as T3 y = (x)
	print y.i, y.j, y.k           
	'' 6, 0, 0
end scope

scope
	'' and this looks wrong, ditto
	dim as T3 y = (cast(t2,x))
	print y.i, y.j, y.k           
	'' 6, 0, 0
end scope

scope
	dim as T1 y = (x)
	print y.i                     
	'' 6
end scope

scope
	dim as T1 y = cast( t2, x )
	print y.i                     
	'' 6
end scope

sleep
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC 1.10.0 Development

Post by fxm »

Up-casting capability well completed.
Thanks Jeff.

Note:
scope
'' explict cast to T2 does nothing due the implicit up-casting to T1
dim as T3 ptr y = new T3(cast(T2, x))
'' 6, 7, 0
'' 6, 0, 0
print y->i, y->j, y->k
delete y
end scope
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: FreeBASIC 1.10.0 Development

Post by coderJeff »

fxm wrote: Dec 28, 2022 14:46 Up-casting capability well completed.
Thanks Jeff.
You are welcome. Though to be honest, the implicit up-casting just doesn't make sense to me and I am disappointed in myself I couldn't solve this.

I understand that it works like it did before, but it doesn't seem correct.

For consistency, I should probably revert the changes to BASE() as well.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC 1.10.0 Development

Post by fxm »

For me the most important thing is that the initializer for constructing a static/dynamic T3 instance works with:
(Type<T3>(x)) '' optional surrounding parentheses but recommended from my point of view
and even:
(Type<T2>(x)) '' surrounding parentheses required
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: FreeBASIC 1.10.0 Development

Post by coderJeff »

Well, this is the thing. A number of improvements were accomplished by not allowing TYPE() to implicitly up-cast at the first opportunity.

The goal I had was this:

Code: Select all

type T1:            i as integer: end type
type T2 extends T1: j as integer: end type
type T3 extends T2: k as integer: end type
dim x as T2 = (6,7)

'' I would really like x to initialize T3.base instead of T1
dim as T3 ptr y = new T3(x, 2)   
'' 6, 2, 0 currently -> but would like 6, 7, 2
print y->i, y->j, y->k
delete y
But fixing above would break behavior where we did want to up-cast:

Code: Select all

type T1:            i as integer: end type
type T2 extends T1: j as integer: end type
type T3 extends T2: k as integer: end type
dim x as T2 = (6,7)

'' need to allow up-casting in this context
dim as T1 ptr y = new T1(x)   
'' 6
print y->i
delete y
EDIT: sorry, made a few edits as I typed this out before I thought through all the details.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC 1.10.0 Development

Post by fxm »

dim as T3 ptr y = new T3(x, 2)
For me, as long as '(Type<T3>(x, 2))' works as initializer, that is enough.
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: FreeBASIC 1.10.0 Development

Post by coderJeff »

I have one more change to make and then I think initializers are looking very good, IMHO. I was able to get improve the up-casting in a way that makes sense to me and I hope it will to users too. A long walk to get there, but I think was worth it. Thank-you for your continued help with all the testing.

Coming up soon:

Code: Select all

    type t1:            i as integer: end type
    type t2 extends t1: j as integer: end type
    type t3 extends t2: k as integer: end type

    dim x3 as T3 = (1,2,3)
    dim p as T1 ptr = new T2(x3)

    '' now in fbc.1.10.0 - x3 is upcasted to T2
    ''                   - new T2() is upcasted to T1 ptr

    ''                              fbc 1.10.0  1.09.0
    print p->i                  ''         1       1
    print cast(T2 ptr, p)->j    ''         2       0
I will put up more examples, after I commit changes.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC 1.10.0 Development

Post by fxm »

Good news.
So I can assume that the second case below will also be fixed ?

Code: Select all

    type t1:            i as integer: end type
    type t2 extends t1: j as integer: end type
    type t3 extends t2: k as integer: end type

    dim x3 as T3 = (1,2,3)
    dim p as T1 ptr = new T2(x3)

    '' now in fbc.1.10.0 - x3 is upcasted to T2
    ''                   - new T2() is upcasted to T1 ptr

    ''                              fbc 1.10.0  1.09.0
    print p->i                  ''         1       1
    print cast(T2 ptr, p)->j    ''         2       0


    dim q as T2 ptr = new T2(x3)
    
    ''                              fbc 1.10.0  1.09.0
    print q->i                 ''         1       1
    print q->j                 ''         2       0
Post Reply