Comment below.

On Fri, Jun 5, 2020 at 2:55 AM Milles, Eric (TR Tech, Content & Ops) <
eric.mil...@thomsonreuters.com> wrote:

> I am suggesting that when a closure is encountered, it is treated like an
> optional branch of the code.  So assignments within alter the inferred type
> the same way an assignment within an if with no else would.  I am not
> looking to examine the code flow and distinguish between closure executed
> immediately and closure that is executed asynchronously or later or not at
> all.  I have this change in hand, but did not want to alter the "second
> pass" checking without discussing it first.
>
> If you are talking about using a union type, I don't think that is the
> safe way to go.  Static compilation is enforcing a typecast to the inferred
> type and this is a runtime error for 9344.
>

I think the union type* is the correct thing here and we should make our
bytecode generation be more sophisticated.

* agree this is a poorly understood concept but we need both union and
intersection types but perhaps we should use different names.

Cheers, Paul.



> class A {}
> class B extends A{ def b() {}}
> class C extends A{}
>
> @CompileStatic
> void test() {
>   def x = new B()
>   ({ x = new C() })()
>   def z = x // new behavior: typeof(x) and typeof(z) is A
>   z.b() // new behavior: STC error because "b()" is not a member of A;
> user would need to use block instead of closure or add a typecast or type
> checking extension
> }
>
> -----Original Message-----
> From: Jochen Theodorou <blackd...@gmx.org>
> Sent: Thursday, June 4, 2020 11:35 AM
> To: dev@groovy.apache.org
> Subject: Re: STC: Closure shared variable assignment handling
>
> On 04.06.20 17:07, Milles, Eric (TR Tech, Content & Ops) wrote:
> > There are a couple open bugs related to STC handling of closure shared
> > variables.  When a shared variable is assigned a value within the
> > closure, the assigned type is not included in the inferred type
> > outside the closure/lambda and this leads to some problems.
> >
> > If this behavior is changed to save the LUB(A,B) as the inferred type
> > of "x", as is suggested in 9516 and seems required by 9344, the second
> > pass checks for method calls will be replaced by no matching method
> errors.
> > That is "Cannot find matching method java.lang.Object#something().
> > Please check if the declared type is correct and if the method exists."
> > will replace "A closure shared variable [x] has been assigned with
> > various types and the method [charAt(int)] does not exist in the
> > lowest upper bound...".* Is this an acceptable change in STC
> > behavior?*
> >
> > @groovy.transform.CompileStatic
> >
> > void test() {
> >
> >    def x = new A();
> >
> >    { -> x = new B() } // may also be called immediately
> >
> >    x.something() // current STC infers typeof(x) == A
> >
> > }
>
> as this is currently failing you are suggesting to let it pass and use the
> type A?
>
> > I think it was done this way because you could define the closure but
> > not call it (like above).  Or call it asynchronously:
> >
> > @groovy.transform.CompileStatic
> >
> > void test() {
> >
> >    def x = new A();
> >
> >    Thread.start { -> x = new B() }
> >
> >    x.something() // current STC infers typeof(x) == A
> >
> > }
>
> would you not agree, just by looking at the realized flow here, that x
> should not be A?
>
> > https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fissu
> > es.apache.org%2Fjira%2Fbrowse%2FGROOVY-9516&amp;data=02%7C01%7Ceric.mi
> > lles%40thomsonreuters.com%7C7b12f318629043d33b9d08d808a54e51%7C62ccb86
> > 46a1a4b5d8e1c397dec1a8258%7C0%7C0%7C637268853358509821&amp;sdata=zoylX
> > L4c9Iic%2FHhoVXxE9iXSV2kp4BBQW2m8wEUYeeM%3D&amp;reserved=0
>
> this is
>
> > class A {}
> > class B extends A{ def b() {}}
> > class C extends A{}
> >
> > @CompileStatic
> > static foo() {
> >   def x = new B()
> >   ({ x = new C() })()
> >   def z = x
> >   z.b()
> > }
>
> ah.. now I get the problem. but to say z is of type C is also wrong.
> What we really need here is not really the LUB in the sense that
> LUB(B,C) = A, iff B extends A && C extends A && B and C are siblings.
> What we would really need is a type A plus all methods defined on B and C.
> I vaguely remember that our LUB calculation does things like that..
> but maybe I remember wrong. If I go by
>
> https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fdocs.groovy-lang.org%2Flatest%2Fhtml%2Fdocumentation%2Fcore-semantics.html%23section-lub&amp;data=02%7C01%7Ceric.milles%40thomsonreuters.com%7C7b12f318629043d33b9d08d808a54e51%7C62ccb8646a1a4b5d8e1c397dec1a8258%7C0%7C0%7C637268853358519815&amp;sdata=NgKVDLj5YZdz43BRn9Du2%2F5yNxoj78BWVWRzAEXSdlk%3D&amp;reserved=0
> then I am wrong. Of course I am well aware, that such a type is difficult
> to realize. And of course the question is how to do the actual method call
> in bytecode. If I cannot cast z to B or B, then I cannot create the
> required invokevirtual. This means this must use an invokedynamic, but a
> different one than we normally use.
>
> What I am trying to say is that we can handle this case as I imagine it,
> but the required implementation work is not small.
>
> > https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fissu
> > es.apache.org%2Fjira%2Fbrowse%2FGROOVY-9344&amp;data=02%7C01%7Ceric.mi
> > lles%40thomsonreuters.com%7C7b12f318629043d33b9d08d808a54e51%7C62ccb86
> > 46a1a4b5d8e1c397dec1a8258%7C0%7C0%7C637268853358519815&amp;sdata=qHXA1
> > 4ljlSoFfGd3Y6FskDqkUkqF4fnOt1T7BTwbnQ8%3D&amp;reserved=0
>
> This is
>
> > class A {}
> > class B {}
> >
> > @groovy.transform.CompileStatic
> > def cs() {
> >     def var
> >     var = new A()
> >     def c = {
> >         var = new B() // Cannot cast object 'B@4e234c52' with class 'B'
> to class 'A'
> >     }
> >     c()
> >     var.toString()
> > }
> >
> > assert cs() != null
>
> which looks to me like a different problem, var is declared with "def", so
> assigning a B should be valid. Only if the static type does no comply with
> the assigned type it should cause an error. There should be also no cast
> inserted.
>
> > https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fissu
> > es.apache.org%2Fjira%2Fbrowse%2FGROOVY-5874&amp;data=02%7C01%7Ceric.mi
> > lles%40thomsonreuters.com%7C7b12f318629043d33b9d08d808a54e51%7C62ccb86
> > 46a1a4b5d8e1c397dec1a8258%7C0%7C0%7C637268853358519815&amp;sdata=oLyIU
> > IbjMuxQCbLXT5RKs0vGxg%2BRH17syiM4dHyWlgg%3D&amp;reserved=0
>
> This is
>
> > @CompileStatic
> >     static main(args)
> >     {
> >         def list = [1, 2, 3]
> >
> >         def sum = 0
> >         list.each { int i -> sum += i }
> >         println(sum)
> >
> >         sum = list.inject(0, { int x, int y -> x + y })
> >         println(sum)
> >     }
>
> what I would imagine here is that in def sum, the flow type for sum is int
> and for list it is List<Integer>. That means sum+=i should change the flow
> type internally to Integer or keep int. The result of the inject should be
> Integer and it should always be allowed to assign it to sum, even if not.
>
> bye Jochen
>
>

Reply via email to