FreeBASIC 1.10.1 Release Discussion
Re: FreeBASIC 1.10.0 Development
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.
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.
Re: FreeBASIC 1.10.0 Development
Yes.
I have noticed this bad behavior (I was waiting a bit to report):
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
Re: FreeBASIC 1.10.0 Development
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:
In case 1, initializers are assigned directly (and is what we tried to fix in last commits):
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:
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.
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
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
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
Re: FreeBASIC 1.10.0 Development
But this seems to work:
Code: Select all
Dim As Parent p1 = Cast(Parent, Type<Child>(c))
Re: FreeBASIC 1.10.0 Development
For the user, this probably looks the same since it is only the difference between an implicit cast and explicit cast.fxm wrote: ↑Dec 26, 2022 18:45 But this seems to work:Code: Select all
Dim As Parent p1 = Cast(Parent, Type<Child>(c))
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.
Re: FreeBASIC 1.10.0 Development
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)').
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)').
Re: FreeBASIC 1.10.0 Development
Thanks, fxm.
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:
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...
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
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
Re: FreeBASIC 1.10.0 Development
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:
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
Re: FreeBASIC 1.10.0 Development
Up-casting capability well completed.
Thanks Jeff.
Note:
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
Re: FreeBASIC 1.10.0 Development
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.
Re: FreeBASIC 1.10.0 Development
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
(Type<T3>(x)) '' optional surrounding parentheses but recommended from my point of view
and even:
(Type<T2>(x)) '' surrounding parentheses required
Re: FreeBASIC 1.10.0 Development
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:
But fixing above would break behavior where we did want to up-cast:
EDIT: sorry, made a few edits as I typed this out before I thought through all the details.
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
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
Re: FreeBASIC 1.10.0 Development
For me, as long as '(Type<T3>(x, 2))' works as initializer, that is enough.dim as T3 ptr y = new T3(x, 2)
Re: FreeBASIC 1.10.0 Development
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:
I will put up more examples, after I commit changes.
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
Re: FreeBASIC 1.10.0 Development
Good news.
So I can assume that the second case below will also be fixed ?
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