From brian.goetz at oracle.com Thu May 2 14:25:18 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 02 May 2013 17:25:18 -0400 Subject: RI update: division of bridging responsibility between VM and compiler In-Reply-To: References: <516C3051.7030200@oracle.com> Message-ID: <5182D9BE.5070207@oracle.com> I like the idea of a "missing bridge detection tool." This seems entirely tractible. This also connects with another idea that was discussed earlier, namely, a migration tool to tell you which defaults were added to which interfaces from one version of a JAR to another, so that subclassers can identify which new methods they want to override. That said, I wouldn't be too scared about the cases under which missing bridges show up; they are likely to be pretty rare (that possibility has already existed since Java 5, and it doesn't happen much, though will be somewhat more likely with default methods.) A lot of things have to align to make it happen; note that they don't happen when one interface covariantly extends another, they require merging of two interface methods from different maintenance domains. What I'd like to do is to tag bridge methods with attributes identifying which method is being bridged to which other method, so the VM can look at the method attributes and say "Oh, this is just a 'symlink' to this other method", and possibly optimize away the bridge stack frame if it can. One of the worst things about the bridge implementation we got stuck with in Java 5 is that they are basically opaque to the VM; there's no semantics to ACC_BRIDGE and so the VM has to assume that the method is ordinary bytecodes. Even if we don't act on that now, it opens the door to future optimizations. For example, if your VM is implemented so that vtable slots are populated by method handles, you could insert bridgee.asType(bridge) directly into the vtable for the bridge, rather than the trampoline generated by javac. But only if you know the semantics, and for that, we need a classfile attribute. On 5/2/2013 4:56 PM, Daniel Heidinga wrote: > We're on board with this proposal. > > After working through the pattern catalog examples, there are some > concerns around whether users will be able to detect when they are > missing bridges. It is common, especially when building large > applications, to take separately compiled libraries and use them without > recompiling (think: no full app recompile when taking a security update > of a particular library). There needs to be some tooling that can > indicate that a bridge method is missing from an application. > > The java compiler (javac) must know how to determine if a bridge is > required. Can we use javac's internals to analyze a classpath and > determine if the classes on the classpath are missing bridges? Either a > new bridge analyzer executable or a javac option (-analyzeBridges?) to > do the detection would allow users to find potential issues and to > understand their applications behaviour. > > Ending up in a world where the answer is "recompile again to be safe" is > bad for the java ecosystem. > > --Dan > > Inactive hide details for Brian Goetz ---04/15/2013 01:16:38 PM---As you > may recall, adding default methods requires that the VBrian Goetz > ---04/15/2013 01:16:38 PM---As you may recall, adding default methods > requires that the VM get involved in default method inher > > > From: > > > Brian Goetz > > To: > > > "lambda-spec-experts at openjdk.java.net" > > > Date: > > > 04/15/2013 01:16 PM > > Subject: > > > RI update: division of bridging responsibility between VM and compiler > > Sent by: > > > lambda-spec-experts-bounces at openjdk.java.net > > ------------------------------------------------------------------------ > > > > As you may recall, adding default methods requires that the VM get > involved in default method inheritance, because it is an explicit goal > for the addition of an interface method with a default to be a > binary-compatible change. We've had an implementation of default > inheritance in the VM for quite a while. The basic inheritance > algorithm was really easy to implement; it built on top of existing > vtable building in a straightforward and well-defined way. > > Some time back, we identified some cases where pushing default > inheritance into the VM seemed to necessitate pushing bridge method > generation into the VM as well. We also have had an implementation of > this in the VM for a while too. But, this is a much bigger change and > we're not as comfortable with it -- it pushes the details of the generic > type system into the VM, and risks exposing Java-language-specific type > system details to classes generated by other language compilers. > > At one point, we were convinced we had no choice. But since then, there > were some simplifications in the definition of overriding with respect > to defaults (specifically, outlawing abstract-default conflicts rather > than silently merging them), and it turns out that this eliminates a > number of the examples that led us to believe we had no choice in this > matter. (Specifically, to land in a corner case, it now requires a > bridge-requiring merge between a class and an interface; can't happen > any more with two interfaces.) After having spent some time trying to > specify what the invoke{virtual,interface,special} semantics might be in > a VM-bridged world -- with the hopes that this would be step 1 along the > path of eventually moving all bridging out of the static compiler (where > it clearly does not belong, and is basically pure technical debt left > over from generics) -- we're getting more comfortable with the corner > cases that we'd have without VM bridging. Indeed, most of them are > analogous to corner cases we already have today and would continue to > have tomorrow under separate compilation with ordinary classes. > > Instead, we're now pursuing a path where we generate bridges into > interfaces (since we can do that now) using an algorithm very similar to > what we do with class bridges. We may need to extend the technique of > compiler-generated bridges with generating additional classfile > attributes that the VM might act on to avoid these anomalies, currently > being explored. > > This offers a significant reduction in complexity. We can rip out all > existing bridge-related code from VM, and do default inheritance using > the simple "same erased signature" overriding the VM has always done. > Can rip out all generic analysis, including verification of generic > signatures. Though might have to add back processing of additional > classfile attributes and potentially use those to modify the behavior of > inheritance, details TBD. And, this keeps the generic type system in > javac, eliminating risks of interference with other language inheritance > semantics. > > > BRIEF NOTATION BREAK > -------------------- > > When we were discussing how to specify default inheritance, we invented > a notation where we wrote things like: > > Cc(Id(Ja)) > > and wrote separate compilation examples as: > > Cc(Id(Ja)) -> Cc(Id(Jd)) > > Which was much easier to reason about, and less ambiguity-prone, than > writing the classes out longhand. Decoder chart: > > A, B: concrete or abstract classes > C: concrete class to be instantiated > I, J, K: interfaces > > In this world, like in FD, there's one method, named "m", with no > arguments. Classes or interfaces have some extra letters after them to > describe how m is declared: > > C -- no declaration of m > Cc -- m() declared in C as concrete > Ca, Ia -- m() declared in C or I as abstract > Id -- m() declared in I as default > Cm -- m() is declared in C as either abstract or concrete > > We now extend this notation with indicators describing covariant > overrides, imagining a linear hierarchy of types T2 <: T1 <: T0: > > Cc0 -- m() declared in C as returning T0 > Cc1 -- m() declared in C as returning T1 > > Supertypes are written in parentheses: > > Cc(Id(Jd)) > > means that C extends I and and I extends J. > > Separate compilation is written as: > > Cc(Id(Ja)) -> Cc(Id(Jd)) > > Since only J is changed, only J is assumed to be recompiled. > > > MOTIVATING EXAMPLE > ------------------ > > Here's a problem we have today (and which the path we'd been pursuing > would not have fixed for 8): > > Cc1(A) -> Cc1(Ac0) > > (This is a "contravariant underride.") This means we go from: > > abstract class A { } > class C <: A { T1 m() { } } > > to > > abstract class A { T0 m() { } } > class C <: A { T1 m() { } } > > without recompiling C. > > What will happen at runtime is: > > m()T1 -> C > m()T0 -> A > > whereas with a global recompile, we would get: > > m()T1 -> C > m()T0 -> C > > Note that: > - This problem exists today and has existed since Java 5 > - Would get no better under the "default VM bridging" plan > - No one seems particularly bothered by this long-standing issue. > > > Now consider the defender analogue of this example: > > Cc1(I) -> Cc1(Id0) > > m()T1 -> C > m()T0 -> I > > Is this any worse than the previous version? For default methods, we > say "classes that don't override this method will get the default, which > by definition meets the contract of I." A moldy class file that had no > idea that it's m()T1 declaration was overriding an as-yet-unborn m()T0 > in a supertype could well be described as "not overriding the method." > In which case they get the default. This does not seem so bad, or any > worse than many other similar separate compilation scenarios today. > > Turning it around, if we handled this case but not the class-based > version of the same issue, might that not even be weirder? > > Note also that with the decision to rule out abstract-default conflicts > (i.e., outlawing K(Ia,Jd)), the set of possible bad cases is reduced a > lot; many of the scary examples came from that space. > > > INTERFACE BRIDGES > ----------------- > > We anticipate that (consistently compiled) interface hierarchies like > > Id1(Jd0) > > will be common. (Consider a method like Collection.immutable(), which > might be covariantly overridden by List.immutable()). So, to support > consistently compiled hierarchies like this (that is, I and J updated > together) without forcing a recompile of concrete classes implementing > I, the compiler could generate a bridge in I redirecting m()T0 to m()T1, > with suitable cast, which is the highest point in the hierarchy where we > can determine a bridge is needed. In a consistently compiled world, > this is all that is needed. > > But we don't live in a consistently compiled world. So we must make > some allowance for what might happen in a separately compiled world. The > current scheme of only compiling bridges into the class where the > bridgee lives helps reduce certain separate compilation artifacts. I > think we should probably continue doing this, so that class bridges > will, at times, override interface bridges. There does not seem to be > harm in this, and it changes fewer things, and eliminates some risk vectors. > > (Ultimately the problem is that compiler bridges suffer from "premature > bytecode". When the compiler generates a bridge, it is trying to reify > the notion of "method m()T1 was known to override method m()T0 at > compile time", but this is opaque to the VM, who can only slavishly > propagate the bridge through subclass vtables as if it were code written > by the user. If, instead of bridges (or in addition to), the compiler > instead generated a class attribute of the form "I believe that m()T1 > overrides m()T0", the VM could act on that information directly, and > this might buy us out of some of the worst possible problems.) > > > WORST CASE SCENARIO > ------------------- > > The cases above are not terrible because the program continues to link > after separation compilation and even does something vaguely > justifiable. Here's a worse scenario (relevant humor break: > http://www.youtube.com/watch?v=_W-qxpN2oEI). > > Cc1(Bc0(Ac0)) -> Cc1(Bc1(Ac0)) > > If the implementation in C does: > > super.m() > > one gets a StackOverflowError. This happens because when we invoke > C.m(), we are really invoking C.m()T1. C.m()T1 invokes B.m()T0 via > invokespecial, thinking that it is invoking the parent implementation. > But really B.m()T0 is a bridge for B.m()T1, that invokes B.m()T1 with > invokevirtual. But B.m()T1 is overridden by C.m()T1, and so the > invokevirtual is dispatched there. Which is where we started, so we > ping-pong between C.m()T1 and B.m()T1 until we fall off the stack. > > Again, note that (a) we already have this problem since Java 5 and (b) > the complex solution we were pursuing would not have fixed it for 8. > But this is definitely worse than the problems above, and we want to not > widen this hole. > > We need to explore further what kinds of separate compilation anomalies > with bridges in interfaces might cause similar problems. > > > EXHAUSTIVE PATTERN CATALOG > -------------------------- > > Dan did a nearly-exhaustive catalog of inheritance scenarios. The > question is, do we find any of these anomalies so bad (worse than > existing anomalies) that we cannot live with them? On review, none of > them seem any worse than the pain of bridge methods under separate > compilation we've been living with for years. > > They are annotated with what happens: > > 0: Description of the behavior of an invocation on an instance of C, > targeting the descriptor of index 0. > 0*: Behavior inconsistent with a full compilation of the final state. > > This following cases are not considered: > - Illegal hierarchies, in either the initial or final state > - Redundant extra classes/interfaces that have no effect on the outcome > - Redundant permutations of 'implements' clauses > - Final states that require recompiling C > > ===== > Linear inheritance (one ancestor, two methods) > > --- > > Cc1(A) -> Cc1(Ac0) > > 0*: Inherited from A > 1: Declared in C > > --- > > Cc1(I) -> Cc1(Id0) > > 0*: Inherited default from I > 1: Declared in C > > ===== > Linear inheritance (two ancestors, no method in C) > > --- > > C(Bc1(A)) -> C(Bc1(Ac0)) > > 0*: Inherited from A > 1: Inherited from B > > --- > > C(B(Ac0)) -> C(Bc1(Ac0)) > > 0: Inherited bridge from B > 1: Inherited from B > > --- > > C(B(A)) -> C(Bc1(Ac0)) > > 0: Inherited bridge from B > 1: Inherited from B > > --- > > C(Ac1(I)) -> C(Ac1(Id0)) > > 0*: Inherited default from I > 1: Inherited from A > > --- > > C(A(Id0)) -> C(Ac1(Id0)) > > 0: Inherited bridge from A > 1: Inherited from A > > --- > > C(A(I)) -> C(Ac1(Id0)) > > 0: Inherited bridge from A > 1: Inherited from A > > --- > > C(Id1(J)) -> C(Id1(Jd0)) > > 0*: Inherited default from J > 1: Inherited default from I > > --- > > C(I(Jd0)) -> C(Id1(Jd0)) > > 0: Inherited bridge from I > 1: Inherited default from I > > --- > > C(I(J)) -> C(Id1(Jd0)) > > 0: Inherited bridge from I > 1: Inherited default from I > > ===== > Linear inheritance (two ancestors, method in C) > > --- > > Cc2(B(Am0)) -> Cc2(Bc1(Am0)) > > 0: Bridge in C > 1*: Inherited from B > 2: Declared in C > > --- > > Cc2(Bm1(A)) -> Cc2(Bm1(Ac0)) > > 0*: Inherited from A > 1: Bridge in C > 2: Declared in C > > --- > > Cc2(B(A)) -> Cc2(Bc1(Ac0)) > > 0*: Inherited bridge from B > 1*: Inherited from B > 2: Declared in C > > --- > > Cc2(A(Im0)) -> Cc2(Ac1(Im0)) > > 0: Bridge in C > 1*: Inherited from A > 2: Declared in C > > --- > > Cc2(Am1(I)) -> Cc2(Am1(Id0)) > > 0*: Inherited from I > 1: Bridge in C > 2: Declared in C > > --- > > Cc2(A(I)) -> Cc2(Ac1(Id0)) > > 0*: Inherited bridge from A > 1*: Inherited from A > 2: Declared in C > > --- > > Cc2(J(Im0)) -> Cc2(Jd1(Im0)) > > 0: Bridge in C > 1*: Inherited default from J > 2: Declared in C > > --- > > Cc2(Jm1(I)) -> Cc2(Jm1(Id0)) > > 0*: Inherited default from I > 1: Bridge in C > 2: Declared in C > > --- > > Cc2(J(I)) -> Cc2(Jd1(Id0)) > > 0*: Inherited bridge from J > 1*: Inherited default from J > 2: Declared in C > > ===== > Independent branches (no method in C) > > --- > > C(Ac1, I) -> C(Ac1, Id0) > > 0*: Inherited default from I > 1: Inherited from A > > --- > > C(A, Id0) -> C(Ac1, Id0) > > 0*: Inherited default from I > 1: Inherited from A > > --- > > C(A, I) -> C(Ac1, Id0) > > 0*: Inherited default from I > 1: Inherited from A > > ===== > Independent branches (method in C) > > --- > > Cc2(Am0, I) -> Cc2(Am0, Id1) > > 0: Bridge in C > 1*: Inherited default from I > 2: Declared in C > > --- > > Cc2(A, Im1) -> Cc2(Ac0, Im1) > > 0*: Inherited from A > 1: Bridge in C > 2: Declared in C > > --- > > Cc2(A, I) -> Cc2(Ac0, Id1) > > 0*: Inherited from A > 1*: Inherited default from I > 2: Declared in C > > --- > > Cc2(Im0, J) -> Cc2(Im0, Jd1) > > 0: Bridge in C > 1*: Inherited default from J > 2: Declared in C > > --- > > Cc2(I, J) -> Cc2(Id0, Jd1) > > 0*: Inherited default from I > 1*: Inherited default from J > 2: Declared in C > > ===== > Diamond branches (no method in C) > > --- > > C(A(Id0), J(Id0)) -> C(Ac1(Id0), J(Id0)) > > 0: Inherited bridge from A > 1: Inherited from A > > --- > > C(A(Id0), J(Id0)) -> C(A(Id0), Jd1(Id0)) > > 0: Inherited bridge from J > 1: Inherited default from J > > --- > > C(A(Id0), J(Id0)) -> C(Ac2(Id0), Jd1(Id0)) > > 0: Inherited bridge from A (beats new bridge in J) > 1*: Inherited default from J > 2: Inherited from A > > --- > > C(J(Id0), K(Id0)) -> C(Jd1(Id0), K(Id0)) > > 0: Inherited bridge from J > 1: Inherited default from J > > --- > > C(Ac2(Im0), J(Im0)) -> C(Ac2(Im0), Jd1(Im0)) > > 0: Inherited bridge from A (beats new bridge in J) > 1*: Inherited default from J > 2: Inherited from A > > --- > > C(A(Im0), Jd1(Im0)) -> C(Ac2(Im0), Jd1(Im0)) > > 0: Inherited bridge from A (beats old bridge in J) > 1*: Inherited default from J > 2: Inherited from A > > ===== > Diamond branches (method in C) > > --- > > Cc2(A(Im0), J(Im0)) -> Cc2(Ac1(Im0), J(Im0)) > > 0: Bridge in C > 1*: Inherited from A > 2: Declared in C > > --- > > Cc2(A(Im0), J(Im0)) -> Cc2(A(Im0), Jd1(Im0)) > > 0: Bridge in C > 1*: Inherited default from J > 2: Declared in C > > --- > > Cc3(A(Im0), J(Im0)) -> Cc3(Ac1(Im0), Jd2(Im0)) > > 0: Bridge in C > 1*: Inherited from A > 2*: Inherited default from J > 3: Declared in C > > --- > > Cc2(J(Im0), K(Im0)) -> Cc2(Jd1(Im0), K(Im0)) > > 0: Bridge in C > 1*: Inherited default from J > 2: Declared in C > > --- > > Cc3(J(Im0), K(Im0)) -> Cc3(Jd1(Im0), Kd2(Im0)) > > 0: Bridge in C > 1*: Inherited default from J > 2*: Inherited default from K > 3: Declared in C > > --- > > Cc3(Am1(Im0), J(Im0)) -> Cc3(Am1(Im0), Jd2(Im0)) > > 0: Bridge in C > 1: Bridge in C > 2*: Inherited default from J > 3: Declared in C > > --- > > Cc3(A(Im0), Jm2(Im0)) -> Cc3(Ac1(Im0), Jm2(Im0)) > > 0: Bridge in C > 1*: Inherited from A > 2: Bridge in C > 3: Declared in C > > --- > > Cc3(Jm1(Im0), K(Im0)) -> Cc3(Jm1(Im0), Kd2(Im0)) > > 0: Bridge in C > 1: Bridge in C > 2*: Inherited default from K > 3: Declared in C > > > From daniel.smith at oracle.com Wed May 8 09:10:27 2013 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 8 May 2013 10:10:27 -0600 Subject: Multi-dimensional array creation references Message-ID: <11E194E2-1C9F-4520-9DE3-AA276399FEE4@oracle.com> We currently have array creation references: Function f = String[]::new; Which are shorthand for lambdas that contain array creation expressions: Function f = i -> new String[i]; The array type may be a multi-dimensional array, and the integer represents the outermost dimension: Function f = String[][]::new; Function f = i -> new String[i][]; We do not support _multiple_ integer parameters: BiFunction f = String[][]::new; // illegal BiFunction f = (i,j) -> new String[i][j]; This was raised on lambda-dev as something to consider supporting. I don't have a strong opinion one way or another; I don't think it's essential, and I don't think this is a particularly common use case, but it also wouldn't be too hard to do. It does carry a small amount of extra complexity. Does anyone have an opinion? I had this to say on lambda-dev: > Method references are essentially shorthand for lambda expressions. We've selected a few common forms of lambda expressions to give them special treatment. But there will always be something just slightly different that lives outside of the set we've chosen to support. And in that case, the solution is to use a lambda expression instead. ?Dan From brian.goetz at oracle.com Wed May 8 10:20:14 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 08 May 2013 13:20:14 -0400 Subject: Multi-dimensional array creation references In-Reply-To: <11E194E2-1C9F-4520-9DE3-AA276399FEE4@oracle.com> References: <11E194E2-1C9F-4520-9DE3-AA276399FEE4@oracle.com> Message-ID: <518A894E.7020207@oracle.com> I'm fine with the current treatment, certainly for now. On 5/8/2013 12:10 PM, Dan Smith wrote: > We currently have array creation references: > > Function f = String[]::new; > > Which are shorthand for lambdas that contain array creation expressions: > > Function f = i -> new String[i]; > > The array type may be a multi-dimensional array, and the integer represents the outermost dimension: > > Function f = String[][]::new; > Function f = i -> new String[i][]; > > We do not support _multiple_ integer parameters: > > BiFunction f = String[][]::new; // illegal > BiFunction f = (i,j) -> new String[i][j]; > > This was raised on lambda-dev as something to consider supporting. I don't have a strong opinion one way or another; I don't think it's essential, and I don't think this is a particularly common use case, but it also wouldn't be too hard to do. It does carry a small amount of extra complexity. > > Does anyone have an opinion? > > I had this to say on lambda-dev: > >> Method references are essentially shorthand for lambda expressions. We've selected a few common forms of lambda expressions to give them special treatment. But there will always be something just slightly different that lives outside of the set we've chosen to support. And in that case, the solution is to use a lambda expression instead. > > ?Dan > From forax at univ-mlv.fr Wed May 8 11:08:52 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 08 May 2013 20:08:52 +0200 Subject: Multi-dimensional array creation references In-Reply-To: <518A894E.7020207@oracle.com> References: <11E194E2-1C9F-4520-9DE3-AA276399FEE4@oracle.com> <518A894E.7020207@oracle.com> Message-ID: <518A94B4.20900@univ-mlv.fr> On 05/08/2013 07:20 PM, Brian Goetz wrote: > I'm fine with the current treatment, certainly for now. I agree with Brian, given that one can use lambda instead of array reference, I'm fine with the current state. R?mi > > On 5/8/2013 12:10 PM, Dan Smith wrote: >> We currently have array creation references: >> >> Function f = String[]::new; >> >> Which are shorthand for lambdas that contain array creation expressions: >> >> Function f = i -> new String[i]; >> >> The array type may be a multi-dimensional array, and the integer >> represents the outermost dimension: >> >> Function f = String[][]::new; >> Function f = i -> new String[i][]; >> >> We do not support _multiple_ integer parameters: >> >> BiFunction f = String[][]::new; // illegal >> BiFunction f = (i,j) -> new String[i][j]; >> >> This was raised on lambda-dev as something to consider supporting. I >> don't have a strong opinion one way or another; I don't think it's >> essential, and I don't think this is a particularly common use case, >> but it also wouldn't be too hard to do. It does carry a small >> amount of extra complexity. >> >> Does anyone have an opinion? >> >> I had this to say on lambda-dev: >> >>> Method references are essentially shorthand for lambda expressions. >>> We've selected a few common forms of lambda expressions to give them >>> special treatment. But there will always be something just slightly >>> different that lives outside of the set we've chosen to support. >>> And in that case, the solution is to use a lambda expression instead. >> >> ?Dan >> From forax at univ-mlv.fr Wed May 8 11:39:46 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 08 May 2013 20:39:46 +0200 Subject: RI update: division of bridging responsibility between VM and compiler In-Reply-To: <516C3051.7030200@oracle.com> References: <516C3051.7030200@oracle.com> Message-ID: <518A9BF2.6040206@univ-mlv.fr> Great if we can avoid the VM to have to be too tied to the Java semantics (especially the generics part). I have a question, do we have an idea of the bridge methods that need to be included in java.util classes becaue we have added default methods on several interfaces ? As I currently understand the problem, I don't think there will be much trouble for code that use the JDK. But for code like Guava collection that uses JDK interfaces and are used in application code, it will be more complex. That said I don't know if Guava introduces new interfaces that use covariant return type or bounded generics. other comments inlined. On 04/15/2013 06:52 PM, Brian Goetz wrote: > As you may recall, adding default methods requires that the VM get > involved in default method inheritance, because it is an explicit goal > for the addition of an interface method with a default to be a > binary-compatible change. We've had an implementation of default > inheritance in the VM for quite a while. The basic inheritance > algorithm was really easy to implement; it built on top of existing > vtable building in a straightforward and well-defined way. > > Some time back, we identified some cases where pushing default > inheritance into the VM seemed to necessitate pushing bridge method > generation into the VM as well. We also have had an implementation of > this in the VM for a while too. But, this is a much bigger change and > we're not as comfortable with it -- it pushes the details of the > generic type system into the VM, and risks exposing > Java-language-specific type system details to classes generated by > other language compilers. > > At one point, we were convinced we had no choice. But since then, > there were some simplifications in the definition of overriding with > respect to defaults (specifically, outlawing abstract-default > conflicts rather than silently merging them), and it turns out that > this eliminates a number of the examples that led us to believe we had > no choice in this matter. (Specifically, to land in a corner case, it > now requires a bridge-requiring merge between a class and an > interface; can't happen any more with two interfaces.) After having > spent some time trying to specify what the > invoke{virtual,interface,special} semantics might be in a VM-bridged > world -- with the hopes that this would be step 1 along the path of > eventually moving all bridging out of the static compiler (where it > clearly does not belong, and is basically pure technical debt left > over from generics) -- we're getting more comfortable with the corner > cases that we'd have without VM bridging. Indeed, most of them are > analogous to corner cases we already have today and would continue to > have tomorrow under separate compilation with ordinary classes. > > Instead, we're now pursuing a path where we generate bridges into > interfaces (since we can do that now) using an algorithm very similar > to what we do with class bridges. We may need to extend the technique > of compiler-generated bridges with generating additional classfile > attributes that the VM might act on to avoid these anomalies, > currently being explored. I don't understand the last sentence ? The bridges are needed by the VM, so what's the point of having the VM ignore them ? > > This offers a significant reduction in complexity. We can rip out all > existing bridge-related code from VM, and do default inheritance using > the simple "same erased signature" overriding the VM has always done. > Can rip out all generic analysis, including verification of generic > signatures. Though might have to add back processing of additional > classfile attributes and potentially use those to modify the behavior > of inheritance, details TBD. And, this keeps the generic type system > in javac, eliminating risks of interference with other language > inheritance semantics. > > > BRIEF NOTATION BREAK > -------------------- > > When we were discussing how to specify default inheritance, we > invented a notation where we wrote things like: > > Cc(Id(Ja)) > > and wrote separate compilation examples as: > > Cc(Id(Ja)) -> Cc(Id(Jd)) > > Which was much easier to reason about, and less ambiguity-prone, than > writing the classes out longhand. Decoder chart: > > A, B: concrete or abstract classes > C: concrete class to be instantiated > I, J, K: interfaces > > In this world, like in FD, there's one method, named "m", with no > arguments. Classes or interfaces have some extra letters after them > to describe how m is declared: > > C -- no declaration of m > Cc -- m() declared in C as concrete > Ca, Ia -- m() declared in C or I as abstract > Id -- m() declared in I as default > Cm -- m() is declared in C as either abstract or concrete > > We now extend this notation with indicators describing covariant > overrides, imagining a linear hierarchy of types T2 <: T1 <: T0: > > Cc0 -- m() declared in C as returning T0 > Cc1 -- m() declared in C as returning T1 > > Supertypes are written in parentheses: > > Cc(Id(Jd)) > > means that C extends I and and I extends J. > > Separate compilation is written as: > > Cc(Id(Ja)) -> Cc(Id(Jd)) > > Since only J is changed, only J is assumed to be recompiled. > > > MOTIVATING EXAMPLE > ------------------ > > Here's a problem we have today (and which the path we'd been pursuing > would not have fixed for 8): > > Cc1(A) -> Cc1(Ac0) > > (This is a "contravariant underride.") This means we go from: > > abstract class A { } > class C <: A { T1 m() { } } > > to > > abstract class A { T0 m() { } } > class C <: A { T1 m() { } } > > without recompiling C. > > What will happen at runtime is: > > m()T1 -> C > m()T0 -> A > > whereas with a global recompile, we would get: > > m()T1 -> C > m()T0 -> C > > Note that: > - This problem exists today and has existed since Java 5 > - Would get no better under the "default VM bridging" plan > - No one seems particularly bothered by this long-standing issue. > > > Now consider the defender analogue of this example: > > Cc1(I) -> Cc1(Id0) > > m()T1 -> C > m()T0 -> I > > Is this any worse than the previous version? For default methods, we > say "classes that don't override this method will get the default, > which by definition meets the contract of I." A moldy class file that > had no idea that it's m()T1 declaration was overriding an > as-yet-unborn m()T0 in a supertype could well be described as "not > overriding the method." In which case they get the default. This does > not seem so bad, or any worse than many other similar separate > compilation scenarios today. > > Turning it around, if we handled this case but not the class-based > version of the same issue, might that not even be weirder? > > Note also that with the decision to rule out abstract-default > conflicts (i.e., outlawing K(Ia,Jd)), the set of possible bad cases is > reduced a lot; many of the scary examples came from that space. > > > INTERFACE BRIDGES > ----------------- > > We anticipate that (consistently compiled) interface hierarchies like > > Id1(Jd0) > > will be common. (Consider a method like Collection.immutable(), which > might be covariantly overridden by List.immutable()). So, to support > consistently compiled hierarchies like this (that is, I and J updated > together) without forcing a recompile of concrete classes implementing > I, the compiler could generate a bridge in I redirecting m()T0 to > m()T1, with suitable cast, which is the highest point in the hierarchy > where we can determine a bridge is needed. In a consistently compiled > world, this is all that is needed. > > But we don't live in a consistently compiled world. So we must make > some allowance for what might happen in a separately compiled world. > The current scheme of only compiling bridges into the class where the > bridgee lives helps reduce certain separate compilation artifacts. I > think we should probably continue doing this, so that class bridges > will, at times, override interface bridges. There does not seem to be > harm in this, and it changes fewer things, and eliminates some risk > vectors. I agree, > > (Ultimately the problem is that compiler bridges suffer from > "premature bytecode". When the compiler generates a bridge, it is > trying to reify the notion of "method m()T1 was known to override > method m()T0 at compile time", but this is opaque to the VM, who can > only slavishly propagate the bridge through subclass vtables as if it > were code written by the user. If, instead of bridges (or in addition > to), the compiler instead generated a class attribute of the form "I > believe that m()T1 overrides m()T0", the VM could act on that > information directly, and this might buy us out of some of the worst > possible problems.) > > > WORST CASE SCENARIO > ------------------- > > The cases above are not terrible because the program continues to link > after separation compilation and even does something vaguely > justifiable. Here's a worse scenario (relevant humor break: > http://www.youtube.com/watch?v=_W-qxpN2oEI). > > Cc1(Bc0(Ac0)) -> Cc1(Bc1(Ac0)) > > If the implementation in C does: > > super.m() > > one gets a StackOverflowError. This happens because when we invoke > C.m(), we are really invoking C.m()T1. C.m()T1 invokes B.m()T0 via > invokespecial, thinking that it is invoking the parent implementation. > But really B.m()T0 is a bridge for B.m()T1, that invokes B.m()T1 with > invokevirtual. But B.m()T1 is overridden by C.m()T1, and so the > invokevirtual is dispatched there. Which is where we started, so we > ping-pong between C.m()T1 and B.m()T1 until we fall off the stack. > > Again, note that (a) we already have this problem since Java 5 and (b) > the complex solution we were pursuing would not have fixed it for 8. > But this is definitely worse than the problems above, and we want to > not widen this hole. > > We need to explore further what kinds of separate compilation > anomalies with bridges in interfaces might cause similar problems. I think the hole is still a little wider because with the actual problem, if you detect the problem you can just don't use the abstract class and it works. With default method in interfaces, there is no way to have this workaround. R?mi > > > EXHAUSTIVE PATTERN CATALOG > -------------------------- > > Dan did a nearly-exhaustive catalog of inheritance scenarios. The > question is, do we find any of these anomalies so bad (worse than > existing anomalies) that we cannot live with them? On review, none of > them seem any worse than the pain of bridge methods under separate > compilation we've been living with for years. > > They are annotated with what happens: > > 0: Description of the behavior of an invocation on an instance of C, > targeting the descriptor of index 0. > 0*: Behavior inconsistent with a full compilation of the final state. > > This following cases are not considered: > - Illegal hierarchies, in either the initial or final state > - Redundant extra classes/interfaces that have no effect on the outcome > - Redundant permutations of 'implements' clauses > - Final states that require recompiling C > > ===== > Linear inheritance (one ancestor, two methods) > > --- > > Cc1(A) -> Cc1(Ac0) > > 0*: Inherited from A > 1: Declared in C > > --- > > Cc1(I) -> Cc1(Id0) > > 0*: Inherited default from I > 1: Declared in C > > ===== > Linear inheritance (two ancestors, no method in C) > > --- > > C(Bc1(A)) -> C(Bc1(Ac0)) > > 0*: Inherited from A > 1: Inherited from B > > --- > > C(B(Ac0)) -> C(Bc1(Ac0)) > > 0: Inherited bridge from B > 1: Inherited from B > > --- > > C(B(A)) -> C(Bc1(Ac0)) > > 0: Inherited bridge from B > 1: Inherited from B > > --- > > C(Ac1(I)) -> C(Ac1(Id0)) > > 0*: Inherited default from I > 1: Inherited from A > > --- > > C(A(Id0)) -> C(Ac1(Id0)) > > 0: Inherited bridge from A > 1: Inherited from A > > --- > > C(A(I)) -> C(Ac1(Id0)) > > 0: Inherited bridge from A > 1: Inherited from A > > --- > > C(Id1(J)) -> C(Id1(Jd0)) > > 0*: Inherited default from J > 1: Inherited default from I > > --- > > C(I(Jd0)) -> C(Id1(Jd0)) > > 0: Inherited bridge from I > 1: Inherited default from I > > --- > > C(I(J)) -> C(Id1(Jd0)) > > 0: Inherited bridge from I > 1: Inherited default from I > > ===== > Linear inheritance (two ancestors, method in C) > > --- > > Cc2(B(Am0)) -> Cc2(Bc1(Am0)) > > 0: Bridge in C > 1*: Inherited from B > 2: Declared in C > > --- > > Cc2(Bm1(A)) -> Cc2(Bm1(Ac0)) > > 0*: Inherited from A > 1: Bridge in C > 2: Declared in C > > --- > > Cc2(B(A)) -> Cc2(Bc1(Ac0)) > > 0*: Inherited bridge from B > 1*: Inherited from B > 2: Declared in C > > --- > > Cc2(A(Im0)) -> Cc2(Ac1(Im0)) > > 0: Bridge in C > 1*: Inherited from A > 2: Declared in C > > --- > > Cc2(Am1(I)) -> Cc2(Am1(Id0)) > > 0*: Inherited from I > 1: Bridge in C > 2: Declared in C > > --- > > Cc2(A(I)) -> Cc2(Ac1(Id0)) > > 0*: Inherited bridge from A > 1*: Inherited from A > 2: Declared in C > > --- > > Cc2(J(Im0)) -> Cc2(Jd1(Im0)) > > 0: Bridge in C > 1*: Inherited default from J > 2: Declared in C > > --- > > Cc2(Jm1(I)) -> Cc2(Jm1(Id0)) > > 0*: Inherited default from I > 1: Bridge in C > 2: Declared in C > > --- > > Cc2(J(I)) -> Cc2(Jd1(Id0)) > > 0*: Inherited bridge from J > 1*: Inherited default from J > 2: Declared in C > > ===== > Independent branches (no method in C) > > --- > > C(Ac1, I) -> C(Ac1, Id0) > > 0*: Inherited default from I > 1: Inherited from A > > --- > > C(A, Id0) -> C(Ac1, Id0) > > 0*: Inherited default from I > 1: Inherited from A > > --- > > C(A, I) -> C(Ac1, Id0) > > 0*: Inherited default from I > 1: Inherited from A > > ===== > Independent branches (method in C) > > --- > > Cc2(Am0, I) -> Cc2(Am0, Id1) > > 0: Bridge in C > 1*: Inherited default from I > 2: Declared in C > > --- > > Cc2(A, Im1) -> Cc2(Ac0, Im1) > > 0*: Inherited from A > 1: Bridge in C > 2: Declared in C > > --- > > Cc2(A, I) -> Cc2(Ac0, Id1) > > 0*: Inherited from A > 1*: Inherited default from I > 2: Declared in C > > --- > > Cc2(Im0, J) -> Cc2(Im0, Jd1) > > 0: Bridge in C > 1*: Inherited default from J > 2: Declared in C > > --- > > Cc2(I, J) -> Cc2(Id0, Jd1) > > 0*: Inherited default from I > 1*: Inherited default from J > 2: Declared in C > > ===== > Diamond branches (no method in C) > > --- > > C(A(Id0), J(Id0)) -> C(Ac1(Id0), J(Id0)) > > 0: Inherited bridge from A > 1: Inherited from A > > --- > > C(A(Id0), J(Id0)) -> C(A(Id0), Jd1(Id0)) > > 0: Inherited bridge from J > 1: Inherited default from J > > --- > > C(A(Id0), J(Id0)) -> C(Ac2(Id0), Jd1(Id0)) > > 0: Inherited bridge from A (beats new bridge in J) > 1*: Inherited default from J > 2: Inherited from A > > --- > > C(J(Id0), K(Id0)) -> C(Jd1(Id0), K(Id0)) > > 0: Inherited bridge from J > 1: Inherited default from J > > --- > > C(Ac2(Im0), J(Im0)) -> C(Ac2(Im0), Jd1(Im0)) > > 0: Inherited bridge from A (beats new bridge in J) > 1*: Inherited default from J > 2: Inherited from A > > --- > > C(A(Im0), Jd1(Im0)) -> C(Ac2(Im0), Jd1(Im0)) > > 0: Inherited bridge from A (beats old bridge in J) > 1*: Inherited default from J > 2: Inherited from A > > ===== > Diamond branches (method in C) > > --- > > Cc2(A(Im0), J(Im0)) -> Cc2(Ac1(Im0), J(Im0)) > > 0: Bridge in C > 1*: Inherited from A > 2: Declared in C > > --- > > Cc2(A(Im0), J(Im0)) -> Cc2(A(Im0), Jd1(Im0)) > > 0: Bridge in C > 1*: Inherited default from J > 2: Declared in C > > --- > > Cc3(A(Im0), J(Im0)) -> Cc3(Ac1(Im0), Jd2(Im0)) > > 0: Bridge in C > 1*: Inherited from A > 2*: Inherited default from J > 3: Declared in C > > --- > > Cc2(J(Im0), K(Im0)) -> Cc2(Jd1(Im0), K(Im0)) > > 0: Bridge in C > 1*: Inherited default from J > 2: Declared in C > > --- > > Cc3(J(Im0), K(Im0)) -> Cc3(Jd1(Im0), Kd2(Im0)) > > 0: Bridge in C > 1*: Inherited default from J > 2*: Inherited default from K > 3: Declared in C > > --- > > Cc3(Am1(Im0), J(Im0)) -> Cc3(Am1(Im0), Jd2(Im0)) > > 0: Bridge in C > 1: Bridge in C > 2*: Inherited default from J > 3: Declared in C > > --- > > Cc3(A(Im0), Jm2(Im0)) -> Cc3(Ac1(Im0), Jm2(Im0)) > > 0: Bridge in C > 1*: Inherited from A > 2: Bridge in C > 3: Declared in C > > --- > > Cc3(Jm1(Im0), K(Im0)) -> Cc3(Jm1(Im0), Kd2(Im0)) > > 0: Bridge in C > 1: Bridge in C > 2*: Inherited default from K > 3: Declared in C From brian.goetz at oracle.com Wed May 8 13:21:52 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 08 May 2013 16:21:52 -0400 Subject: RI update: division of bridging responsibility between VM and compiler In-Reply-To: <518A9BF2.6040206@univ-mlv.fr> References: <516C3051.7030200@oracle.com> <518A9BF2.6040206@univ-mlv.fr> Message-ID: <518AB3E0.7040908@oracle.com> > I have a question, do we have an idea of the bridge methods that need to > be included in java.util classes becaue we have added default methods on > several interfaces ? We don't, but we should be able to measure that pretty easily -- write an ASM program to crawl the JDK and emit a diagnostic for every ACC_BRIDGE method it finds. > As I currently understand the problem, I don't think there will be much > trouble for code that use the JDK. > But for code like Guava collection that uses JDK interfaces and are used > in application code, it will be more complex. > That said I don't know if Guava introduces new interfaces that use > covariant return type or bounded generics. Constructs like: interface FooPredicate extends Predicate { ... } seem likely enough in third party libraries. In this case, javac would generate the bridge into FooPredicate at compile time. >> Instead, we're now pursuing a path where we generate bridges into >> interfaces (since we can do that now) using an algorithm very similar >> to what we do with class bridges. We may need to extend the technique >> of compiler-generated bridges with generating additional classfile >> attributes that the VM might act on to avoid these anomalies, >> currently being explored. > > I don't understand the last sentence ? > The bridges are needed by the VM, so what's the point of having the VM > ignore them ? The fundamental problem that bridges address is: the notion of inheritance implemented by the Java language and by the JVM are different. What appears to be two methods to the JVM may or may not be the "same" method in Java. Bridges paper over this difference by generating bytecode that the VM dumbly interprets. But if the VM *knew* that method A was a bridge for method B, it could do a better job. For example, imagine a VM implementation where the vtable contained MHs. Instead of building bytecode for method A that just does a (potentially virtual) call to method B, we could populate the slot for A with B.asType(A). This could peel one layer off the call stack. The last sentence was appealing to the notion that we could tag the bridge method with an attribute that made its semantics scrutable to the VM, so the VM could use this information to optimize. (Think "symlink"). We have no plans to act on this information immediately, but reifying information about the difference between language-level inheritance and VM-level inheritance seems better than throwing this information away and then trying to reconstruct it. From forax at univ-mlv.fr Thu May 9 05:25:46 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 09 May 2013 14:25:46 +0200 Subject: RI update: division of bridging responsibility between VM and compiler In-Reply-To: <518AB3E0.7040908@oracle.com> References: <516C3051.7030200@oracle.com> <518A9BF2.6040206@univ-mlv.fr> <518AB3E0.7040908@oracle.com> Message-ID: <518B95CA.9020504@univ-mlv.fr> On 05/08/2013 10:21 PM, Brian Goetz wrote: >> I have a question, do we have an idea of the bridge methods that need to >> be included in java.util classes becaue we have added default methods on >> several interfaces ? > > We don't, but we should be able to measure that pretty easily -- write > an ASM program to crawl the JDK and emit a diagnostic for every > ACC_BRIDGE method it finds. in b88, the program you suggest found no such bridges in java.util (or in the whole JDK). > >> As I currently understand the problem, I don't think there will be much >> trouble for code that use the JDK. >> But for code like Guava collection that uses JDK interfaces and are used >> in application code, it will be more complex. >> That said I don't know if Guava introduces new interfaces that use >> covariant return type or bounded generics. > > Constructs like: > > interface FooPredicate extends Predicate { ... } > > seem likely enough in third party libraries. In this case, javac > would generate the bridge into FooPredicate at compile time. I more worry about existing third party libraries that uses already existing interfaces like List or Map because these libraries may need to be re-compiled. I agree that they need to be recompiled only in a corner case, the third party library need to extends an already existing interface and have a public method with a name and parameter types that make the method an override of a default method listed below, but this corner case will appear. java/util/concurrent/ConcurrentMap default getOrDefault(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; java/util/Comparator default reverseOrder()Ljava/util/Comparator; java/util/Comparator default thenComparing(Ljava/util/Comparator;)Ljava/util/Comparator; java/util/Map default getOrDefault(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; java/util/Map default putIfAbsent(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; java/util/Map default replace(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z java/util/Map default replace(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; By example, a comparator defined like this will cause trouble: public class FooComparator extends Comparator { public FooComparator reverseOrder() { ... } } this code must be be recompiled because javac need to insert a bridge. That said, a note about this in the compatibility issues guide of the jdk8 is enough in my opinion. > > >>> Instead, we're now pursuing a path where we generate bridges into >>> interfaces (since we can do that now) using an algorithm very similar >>> to what we do with class bridges. We may need to extend the technique >>> of compiler-generated bridges with generating additional classfile >>> attributes that the VM might act on to avoid these anomalies, >>> currently being explored. >> >> I don't understand the last sentence ? >> The bridges are needed by the VM, so what's the point of having the VM >> ignore them ? > > The fundamental problem that bridges address is: the notion of > inheritance implemented by the Java language and by the JVM are > different. What appears to be two methods to the JVM may or may not > be the "same" method in Java. > > Bridges paper over this difference by generating bytecode that the VM > dumbly interprets. But if the VM *knew* that method A was a bridge > for method B, it could do a better job. For example, imagine a VM > implementation where the vtable contained MHs. Instead of building > bytecode for method A that just does a (potentially virtual) call to > method B, we could populate the slot for A with B.asType(A). This > could peel one layer off the call stack. A bridge is a method which is not overriden usually (unless you override the type with a raw type), so if the VM is able to inline the call in the wonderful world where no bridge is needed, it should be able to inline the call in the real world with bridges. As a trivia, there is a case where the de-virtualisation actually works, and will not if bridges are not generated : abstract class A { abstract void m(T t) { ... } } class B extends A { void m(T t) { ... } } class C extends A { void m(C c) { ... } } class D extends C { void m(C c) { ... } } for a callsite A a = ... , a.m(t), here we have 3 methods B.m, C.m and D.m so if a is effectively a B a C or a D, the VM will not de-virtualize it in the wonderful world without bridges. But because we have bridges, a.m(t) can only call B.m(Object) or C.m(Object) [the bridge] and inside the bridge it will dispatch again on C or D. The VM will generate a if/else for the first call and another if/else for the call inside the bridge de-virtualising the hierarchy. > > The last sentence was appealing to the notion that we could tag the > bridge method with an attribute that made its semantics scrutable to > the VM, so the VM could use this information to optimize. (Think > "symlink"). There is already the access modifier ACC_BRIDGE and bridge methods are so small that the VM always try to inline them, so there is no need of a special marker IMO. > > We have no plans to act on this information immediately, but reifying > information about the difference between language-level inheritance > and VM-level inheritance seems better than throwing this information > away and then trying to reconstruct it. > > R?mi From daniel.smith at oracle.com Thu May 9 07:07:41 2013 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 9 May 2013 08:07:41 -0600 Subject: RI update: division of bridging responsibility between VM and compiler In-Reply-To: <518B95CA.9020504@univ-mlv.fr> References: <516C3051.7030200@oracle.com> <518A9BF2.6040206@univ-mlv.fr> <518AB3E0.7040908@oracle.com> <518B95CA.9020504@univ-mlv.fr> Message-ID: <9F565D6C-2503-4CDE-AFD5-23D64B580F39@oracle.com> On May 9, 2013, at 6:25 AM, Remi Forax wrote: > I more worry about existing third party libraries that uses already existing interfaces like List or Map > because these libraries may need to be re-compiled. > I agree that they need to be recompiled only in a corner case, > the third party library need to extends an already existing interface and have a public method > with a name and parameter types that make the method an override of a default method listed below, > but this corner case will appear. > > java/util/concurrent/ConcurrentMap default getOrDefault(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; > java/util/Comparator default reverseOrder()Ljava/util/Comparator; > java/util/Comparator default thenComparing(Ljava/util/Comparator;)Ljava/util/Comparator; > java/util/Map default getOrDefault(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; > java/util/Map default putIfAbsent(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; > java/util/Map default replace(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z > java/util/Map default replace(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; > > By example, a comparator defined like this will cause trouble: > public class FooComparator extends Comparator { > public FooComparator reverseOrder() { ... } > } > > this code must be be recompiled because javac need to insert a bridge. > > That said, a note about this in the compatibility issues guide of the jdk8 is enough in my opinion. But keep in mind: you're anticipating a scenario in which the client's method already existed _before_ the default methods were added, and it's only a coincidence that they happen to share the same name and have a compatible signature/return (but different erased signature/return). As you say, a corner case, but I want to emphasize how rare this should be. Even then: the failure mode is to simply use the default method instead of the specialized method. Within the obscure space we're talking about, what are the chances that the default method doesn't already do the same thing as the specialized method? Agreed that the release notes should mention the fact that new default methods in the API may share a name with methods that already exist in client code, and describe potential consequences. ?Dan From brian.goetz at oracle.com Thu May 9 08:56:54 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 09 May 2013 11:56:54 -0400 Subject: RI update: division of bridging responsibility between VM and compiler In-Reply-To: <518B95CA.9020504@univ-mlv.fr> References: <516C3051.7030200@oracle.com> <518A9BF2.6040206@univ-mlv.fr> <518AB3E0.7040908@oracle.com> <518B95CA.9020504@univ-mlv.fr> Message-ID: <518BC746.7070904@oracle.com> > I more worry about existing third party libraries that uses already > existing interfaces like List or Map > because these libraries may need to be re-compiled. > I agree that they need to be recompiled only in a corner case, > the third party library need to extends an already existing interface > and have a public method > with a name and parameter types that make the method an override of a > default method listed below, > but this corner case will appear. OK, but bear in mind that this is a problem we've had since Java 5, when the abomination that is bridges was deemed to be a good idea. We're not creating new problems here, and the existing problems have not turned out to be big problems in practice. (We've been in "recompile your code" mode since Java 5, but the number of times people have actually had to is quite small, small enough to make us all believe we're not actually in that mode.) > By example, a comparator defined like this will cause trouble: > public class FooComparator extends Comparator { > public FooComparator reverseOrder() { ... } > } > > this code must be be recompiled because javac need to insert a bridge. Try it with the lambda compiler! We guard against this case, and it works fine. Decompile the code to find out how :) From forax at univ-mlv.fr Thu May 9 11:00:27 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 09 May 2013 20:00:27 +0200 Subject: RI update: division of bridging responsibility between VM and compiler In-Reply-To: <518BC746.7070904@oracle.com> References: <516C3051.7030200@oracle.com> <518A9BF2.6040206@univ-mlv.fr> <518AB3E0.7040908@oracle.com> <518B95CA.9020504@univ-mlv.fr> <518BC746.7070904@oracle.com> Message-ID: <518BE43B.6000104@univ-mlv.fr> On 05/09/2013 05:56 PM, Brian Goetz wrote: >> I more worry about existing third party libraries that uses already >> existing interfaces like List or Map >> because these libraries may need to be re-compiled. >> I agree that they need to be recompiled only in a corner case, >> the third party library need to extends an already existing interface >> and have a public method >> with a name and parameter types that make the method an override of a >> default method listed below, >> but this corner case will appear. > > OK, but bear in mind that this is a problem we've had since Java 5, > when the abomination that is bridges was deemed to be a good idea. > We're not creating new problems here, and the existing problems have > not turned out to be big problems in practice. (We've been in > "recompile your code" mode since Java 5, but the number of times > people have actually had to is quite small, small enough to make us > all believe we're not actually in that mode.) > >> By example, a comparator defined like this will cause trouble: >> public class FooComparator extends Comparator { >> public FooComparator reverseOrder() { ... } >> } >> >> this code must be be recompiled because javac need to insert a bridge. > > Try it with the lambda compiler! We guard against this case, and it > works fine. Decompile the code to find out how :) > ??, the point is that users will need to recompile, and also currently this code works with b88 because the code that implement bridges in the VM is still present. I've also found that javac of b88 doesn't mark bridge in interface as BRIDGE, but current version of javac mark bridge as BRIDGE so I will wait b89 to see if bridges in interfaces are generated in the jdk R?mi From brian.goetz at oracle.com Thu May 9 11:31:36 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 09 May 2013 14:31:36 -0400 Subject: RI update: division of bridging responsibility between VM and compiler In-Reply-To: <518BE43B.6000104@univ-mlv.fr> References: <516C3051.7030200@oracle.com> <518A9BF2.6040206@univ-mlv.fr> <518AB3E0.7040908@oracle.com> <518B95CA.9020504@univ-mlv.fr> <518BC746.7070904@oracle.com> <518BE43B.6000104@univ-mlv.fr> Message-ID: <518BEB88.3030200@oracle.com> >>> By example, a comparator defined like this will cause trouble: >>> public class FooComparator extends Comparator { >>> public FooComparator reverseOrder() { ... } >>> } >>> >>> this code must be be recompiled because javac need to insert a bridge. >> >> Try it with the lambda compiler! We guard against this case, and it >> works fine. Decompile the code to find out how :) > > ??, > the point is that users will need to recompile, and also currently this > code works with b88 > because the code that implement bridges in the VM is still present. No, the user should have no need to recompile in this case. If the compiler detects that the interface to which a lambda is being converted lacks the needed bridges, it takes corrective action in the translation process, to ensure the needed bridges are injected. > I've also found that javac of b88 doesn't mark bridge in interface as > BRIDGE, > but current version of javac mark bridge as BRIDGE so I will wait b89 > to see if bridges in interfaces are generated in the jdk I'll log this as a bug, thanks. From maurizio.cimadamore at oracle.com Thu May 9 13:56:48 2013 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Thu, 09 May 2013 21:56:48 +0100 Subject: RI update: division of bridging responsibility between VM and compiler In-Reply-To: <518BEB88.3030200@oracle.com> References: <516C3051.7030200@oracle.com> <518A9BF2.6040206@univ-mlv.fr> <518AB3E0.7040908@oracle.com> <518B95CA.9020504@univ-mlv.fr> <518BC746.7070904@oracle.com> <518BE43B.6000104@univ-mlv.fr> <518BEB88.3030200@oracle.com> Message-ID: <518C0D90.4040402@oracle.com> With latest compiler I get this: interface A { Object m(); } interface B extends A { Integer m(); } javap output: { public abstract java.lang.Integer m(); descriptor: ()Ljava/lang/Integer; flags: ACC_PUBLIC, ACC_ABSTRACT public java.lang.Object m(); descriptor: ()Ljava/lang/Object; flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokeinterface #1, 1 // InterfaceMethod m:()Ljava/lang/Integer; 6: areturn LineNumberTable: line 5: 0 } Since the bridging logic is being reused it seems strange that javac is generating bridges without the ACC_BRIDGE flag. Could you post an example where this is not the case? Maurizio On 09/05/13 19:31, Brian Goetz wrote: >>>> By example, a comparator defined like this will cause trouble: >>>> public class FooComparator extends Comparator { >>>> public FooComparator reverseOrder() { ... } >>>> } >>>> >>>> this code must be be recompiled because javac need to insert a bridge. >>> >>> Try it with the lambda compiler! We guard against this case, and it >>> works fine. Decompile the code to find out how :) >> >> ??, >> the point is that users will need to recompile, and also currently this >> code works with b88 >> because the code that implement bridges in the VM is still present. > > No, the user should have no need to recompile in this case. If the > compiler detects that the interface to which a lambda is being > converted lacks the needed bridges, it takes corrective action in the > translation process, to ensure the needed bridges are injected. > >> I've also found that javac of b88 doesn't mark bridge in interface as >> BRIDGE, >> but current version of javac mark bridge as BRIDGE so I will wait b89 >> to see if bridges in interfaces are generated in the jdk > > I'll log this as a bug, thanks. > > From forax at univ-mlv.fr Fri May 10 02:30:16 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Fri, 10 May 2013 11:30:16 +0200 Subject: RI update: division of bridging responsibility between VM and compiler In-Reply-To: <518C0D90.4040402@oracle.com> References: <516C3051.7030200@oracle.com> <518A9BF2.6040206@univ-mlv.fr> <518AB3E0.7040908@oracle.com> <518B95CA.9020504@univ-mlv.fr> <518BC746.7070904@oracle.com> <518BE43B.6000104@univ-mlv.fr> <518BEB88.3030200@oracle.com> <518C0D90.4040402@oracle.com> Message-ID: <518CBE28.8060803@univ-mlv.fr> On 05/09/2013 10:56 PM, Maurizio Cimadamore wrote: > With latest compiler I get this: > > interface A { > Object m(); > } > > interface B extends A { > Integer m(); > } > > > > javap output: > > { > public abstract java.lang.Integer m(); > descriptor: ()Ljava/lang/Integer; > flags: ACC_PUBLIC, ACC_ABSTRACT > > public java.lang.Object m(); > descriptor: ()Ljava/lang/Object; > flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC > Code: > stack=1, locals=1, args_size=1 > 0: aload_0 > 1: invokeinterface #1, 1 // InterfaceMethod > m:()Ljava/lang/Integer; > 6: areturn > LineNumberTable: > line 5: 0 > } > > Since the bridging logic is being reused it seems strange that javac > is generating bridges without the ACC_BRIDGE flag. Could you post an > example where this is not the case? > > Maurizio There is no bug, it's just that the version used by jdk8b88 doesn't mark bridge as ACC_BRIDGE, as you say newer version does. My first question was does the jdk has bridge in interfaces somewhere, but because I've only tested with b88, I can not answer to that question. I will wait b89 for that. R?mi > > On 09/05/13 19:31, Brian Goetz wrote: >>>>> By example, a comparator defined like this will cause trouble: >>>>> public class FooComparator extends Comparator { >>>>> public FooComparator reverseOrder() { ... } >>>>> } >>>>> >>>>> this code must be be recompiled because javac need to insert a >>>>> bridge. >>>> >>>> Try it with the lambda compiler! We guard against this case, and it >>>> works fine. Decompile the code to find out how :) >>> >>> ??, >>> the point is that users will need to recompile, and also currently this >>> code works with b88 >>> because the code that implement bridges in the VM is still present. >> >> No, the user should have no need to recompile in this case. If the >> compiler detects that the interface to which a lambda is being >> converted lacks the needed bridges, it takes corrective action in the >> translation process, to ensure the needed bridges are injected. >> >>> I've also found that javac of b88 doesn't mark bridge in interface as >>> BRIDGE, >>> but current version of javac mark bridge as BRIDGE so I will wait b89 >>> to see if bridges in interfaces are generated in the jdk >> >> I'll log this as a bug, thanks. >> >> > From brian.goetz at oracle.com Tue May 14 16:50:37 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 14 May 2013 19:50:37 -0400 Subject: Explicit public on static interface methods? Message-ID: <5192CDCD.1060508@oracle.com> John Rose made this observation/suggestion: Implicit public considered risky: After working for a while on a new interface, I realized that all the methods I coded without an explicit "public" keyword were still public. I was using them as package privates shared between the interface and some package-private implementation logic. Oops. Did not discover this until I was preparing a javadoc for public review. That feels a little too late. Our processes would block me from accidentally introducing a public static, but that might not be true of customers. Suggest requiring a "public" on "static" interface members. Will reduce confusion if we allow non-public statics in the future. From david.holmes at oracle.com Tue May 14 17:11:09 2013 From: david.holmes at oracle.com (David Holmes) Date: Wed, 15 May 2013 10:11:09 +1000 Subject: Explicit public on static interface methods? In-Reply-To: <5192CDCD.1060508@oracle.com> References: <5192CDCD.1060508@oracle.com> Message-ID: <5192D29D.10401@oracle.com> On 15/05/2013 9:50 AM, Brian Goetz wrote: > John Rose made this observation/suggestion: > > Implicit public considered risky: After working for a while on a new > interface, I realized that all the methods I coded without an explicit > "public" keyword were still public. I was using them as package > privates shared between the interface and some package-private > implementation logic. Oops. Did not discover this until I was > preparing a javadoc for public review. That feels a little too late. > Our processes would block me from accidentally introducing a public > static, but that might not be true of customers. Obviously some education needed that the Java language only supports public interface members at this time. ;-) > Suggest requiring a "public" on "static" interface members. Will reduce > confusion if we allow non-public statics in the future. Didn't we already paint this bikeshed? Right now we only allow public members and the convention is that the public is implicit. If we were to change that then we should change it across the board - there is nothing special about static methods. David From david.holmes at oracle.com Tue May 14 18:40:58 2013 From: david.holmes at oracle.com (David Holmes) Date: Wed, 15 May 2013 11:40:58 +1000 Subject: Explicit public on static interface methods? In-Reply-To: <5192D29D.10401@oracle.com> References: <5192CDCD.1060508@oracle.com> <5192D29D.10401@oracle.com> Message-ID: <5192E7AA.10103@oracle.com> On 15/05/2013 10:11 AM, David Holmes wrote: > On 15/05/2013 9:50 AM, Brian Goetz wrote: >> John Rose made this observation/suggestion: >> >> Implicit public considered risky: After working for a while on a new >> interface, I realized that all the methods I coded without an explicit >> "public" keyword were still public. I was using them as package >> privates shared between the interface and some package-private >> implementation logic. Oops. Did not discover this until I was >> preparing a javadoc for public review. That feels a little too late. >> Our processes would block me from accidentally introducing a public >> static, but that might not be true of customers. > > Obviously some education needed that the Java language only supports > public interface members at this time. ;-) Sorry that can be read the wrong way - no slight to John intended. Java presently only allows public interface members and interface methods are abstract. Java 8 adds the ability to define non-abstract methods. There is no change to the allowed accessibility so no reason to assume that you can write package-private "implementation" details in an interface. An interface is not a class and any Java programmer, existing or new, should be aware of the distinction. Of course now the distinction is far less clear than it was. >> Suggest requiring a "public" on "static" interface members. Will reduce >> confusion if we allow non-public statics in the future. > > Didn't we already paint this bikeshed? Right now we only allow public > members and the convention is that the public is implicit. If we were to > change that then we should change it across the board - there is nothing > special about static methods. I think it far more likely to evoke confusion if we have arbitrarily different rules based on concrete vs abstract method. But may this is a transition path to requiring access modifiers in Java 9. David > David > > > From andrey.breslav at jetbrains.com Tue May 14 21:27:01 2013 From: andrey.breslav at jetbrains.com (Andrey Breslav) Date: Wed, 15 May 2013 08:27:01 +0400 Subject: Explicit public on static interface methods? In-Reply-To: <5192CDCD.1060508@oracle.com> References: <5192CDCD.1060508@oracle.com> Message-ID: > John Rose made this observation/suggestion: > > Implicit public considered risky: After working for a while on a new interface, I realized that all the methods I coded without an explicit "public" keyword were still public. I was using them as package privates shared between the interface and some package-private implementation logic. Oops. Did not discover this until I was preparing a javadoc for public review. That feels a little too late. Our processes would block me from accidentally introducing a public static, but that might not be true of customers. > > Suggest requiring a "public" on "static" interface members. Will reduce confusion if we allow non-public statics in the future. > Observation 1: We will never be able to have package-private members in interfaces, unless we require explicit public now. Observation 2: Having static methods and fields inconsistently handled for interfaces looks ugly. Fields are public by default, so making methods package-private by default would look odd. (By observation 1, we can't have package-private fields in interfaces). From forax at univ-mlv.fr Wed May 15 04:26:48 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 15 May 2013 13:26:48 +0200 Subject: Explicit public on static interface methods? In-Reply-To: References: <5192CDCD.1060508@oracle.com> Message-ID: <519370F8.3030109@univ-mlv.fr> On 05/15/2013 06:27 AM, Andrey Breslav wrote: >> John Rose made this observation/suggestion: >> >> Implicit public considered risky: After working for a while on a new interface, I realized that all the methods I coded without an explicit "public" keyword were still public. I was using them as package privates shared between the interface and some package-private implementation logic. Oops. Did not discover this until I was preparing a javadoc for public review. That feels a little too late. Our processes would block me from accidentally introducing a public static, but that might not be true of customers. >> >> Suggest requiring a "public" on "static" interface members. Will reduce confusion if we allow non-public statics in the future. >> > Observation 1: We will never be able to have package-private members in interfaces, unless we require explicit public now. or we accept 'package' as a valid keyword for access modifiers. > Observation 2: Having static methods and fields inconsistently handled for interfaces looks ugly. Fields are public by default, so making methods package-private by default would look odd. (By observation 1, we can't have package-private fields in interfaces). yes. In my opinion, the best is to stay with public as the default modifiers in interface and modify the code convention to say that members of an interface should be declared public with the public modifier so IDEs can emit a warning saying that a method with no modifier doesn't respect the code convention. R?mi From Daniel_Heidinga at ca.ibm.com Tue May 21 18:58:52 2013 From: Daniel_Heidinga at ca.ibm.com (Daniel Heidinga) Date: Tue, 21 May 2013 21:58:52 -0400 Subject: Explicit public on static interface methods? In-Reply-To: <519370F8.3030109@univ-mlv.fr> References: <5192CDCD.1060508@oracle.com> <519370F8.3030109@univ-mlv.fr> Message-ID: > In my opinion, the best is to stay with public as the default modifiers > in interface +1. We debated this internally for quite a while before coming to the conclusion that none of the arguments for being inconsistent (clarity, avoid copy/paste errors, etc) were strong enough for us to advocate making static methods in interface inconsistent with everything else in interfaces. -------------- next part -------------- An HTML attachment was scrubbed... URL: http://mail.openjdk.java.net/pipermail/lambda-spec-experts/attachments/20130521/a9316914/attachment.html From forax at univ-mlv.fr Wed May 22 00:45:37 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 22 May 2013 09:45:37 +0200 Subject: Compilation of lambda in an interface, do we have a plan ? Message-ID: <519C77A1.6090702@univ-mlv.fr> The compiler doesn't like code like this: public interface Foo { default void bar() { Runnable r = () -> { System.out.println(this); }; } } in that case the lambda in bar can be compiled to a static method. But what if the lambda calls a super method like this: interface I { default void m() { } } public interface Foo extends I{ default void bar() { Runnable r = () -> { I.super.m(); }; } } given that I.super.m() is translated to an invokespecial call and that as far as I know invokespecial needs to be called in an instance method. One possible translation is to translate the lambda to a default method but given that a default method is public, an interface that extends the interface may be able to override the code of the lambda with another lambda declared in the class with the same signature even if the two lambdas are unrelated. does it means that the lambda in interfaces must be numbered knowing the whole hierarchy ? cheers, R?mi From maurizio.cimadamore at oracle.com Wed May 22 03:29:46 2013 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Wed, 22 May 2013 11:29:46 +0100 Subject: Compilation of lambda in an interface, do we have a plan ? In-Reply-To: <519C77A1.6090702@univ-mlv.fr> References: <519C77A1.6090702@univ-mlv.fr> Message-ID: <519C9E1A.3060201@oracle.com> I'm not sure as to why this hasn't been fixed by Robert recent changes - Robert? I believe the plan is to go static for the first example, and default _private_ for the second. Maurizio On 22/05/13 08:45, Remi Forax wrote: > The compiler doesn't like code like this: > > public interface Foo { > default void bar() { > Runnable r = () -> { > System.out.println(this); > }; > } > } > > in that case the lambda in bar can be compiled to a static method. > > But what if the lambda calls a super method like this: > interface I { > default void m() { } > } > > public interface Foo extends I{ > default void bar() { > Runnable r = () -> { > I.super.m(); > }; > } > } > > given that I.super.m() is translated to an invokespecial call and that > as far as I know invokespecial needs to be called in an instance method. > > One possible translation is to translate the lambda to a default method > but given that a default method is public, an interface that extends > the interface may be able to override the code of the lambda > with another lambda declared in the class with the same signature > even if the two lambdas are unrelated. > > does it means that the lambda in interfaces must be numbered knowing > the whole hierarchy ? > > cheers, > R?mi > From forax at univ-mlv.fr Wed May 22 06:29:31 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 22 May 2013 15:29:31 +0200 Subject: Compilation of lambda in an interface, do we have a plan ? In-Reply-To: <519C9E1A.3060201@oracle.com> References: <519C77A1.6090702@univ-mlv.fr> <519C9E1A.3060201@oracle.com> Message-ID: <519CC83B.5020901@univ-mlv.fr> On 05/22/2013 12:29 PM, Maurizio Cimadamore wrote: > I'm not sure as to why this hasn't been fixed by Robert recent changes > - Robert? It seems that Robert as done it's change on jdk8/tl/langtools and not lambda/lambda/langtools, so the version of javac from jdk8/tl is more recent that the one from lambda/lambda/langtools. > > I believe the plan is to go static for the first example, and default > _private_ for the second. Ok, I think that in that case lambda in class should be translated the same way. Currently, a lambda that capture this but doesn't use super is not translated to a static method. > > Maurizio R?mi > > On 22/05/13 08:45, Remi Forax wrote: >> The compiler doesn't like code like this: >> >> public interface Foo { >> default void bar() { >> Runnable r = () -> { >> System.out.println(this); >> }; >> } >> } >> >> in that case the lambda in bar can be compiled to a static method. >> >> But what if the lambda calls a super method like this: >> interface I { >> default void m() { } >> } >> >> public interface Foo extends I{ >> default void bar() { >> Runnable r = () -> { >> I.super.m(); >> }; >> } >> } >> >> given that I.super.m() is translated to an invokespecial call and that >> as far as I know invokespecial needs to be called in an instance method. >> >> One possible translation is to translate the lambda to a default method >> but given that a default method is public, an interface that extends >> the interface may be able to override the code of the lambda >> with another lambda declared in the class with the same signature >> even if the two lambdas are unrelated. >> >> does it means that the lambda in interfaces must be numbered knowing >> the whole hierarchy ? >> >> cheers, >> R?mi >> > From Robert.Field at oracle.com Wed May 22 11:27:53 2013 From: Robert.Field at oracle.com (Robert Field) Date: Wed, 22 May 2013 11:27:53 -0700 Subject: Compilation of lambda in an interface, do we have a plan ? In-Reply-To: <519CC83B.5020901@univ-mlv.fr> References: <519C77A1.6090702@univ-mlv.fr> <519C9E1A.3060201@oracle.com> <519CC83B.5020901@univ-mlv.fr> Message-ID: <519D0E29.20000@oracle.com> On 5/22/13 6:29 AM, Remi Forax wrote: > On 05/22/2013 12:29 PM, Maurizio Cimadamore wrote: >> I'm not sure as to why this hasn't been fixed by Robert recent >> changes - Robert? > > It seems that Robert as done it's change on jdk8/tl/langtools and not > lambda/lambda/langtools, > so the version of javac from jdk8/tl is more recent that the one from > lambda/lambda/langtools. Making interface lambda methods static where possible was done in lambda weeks back. There are some other changes which have not been sync'ed back to lambda. > >> >> I believe the plan is to go static for the first example, and default >> _private_ for the second. > > Ok, > I think that in that case lambda in class should be translated the > same way. > Currently, a lambda that capture this but doesn't use super is not > translated to a static method. The plan is (1) for all lambda methods to be private. (2) where there is no implicit/explicit this reference, the lambda method is static. (3) otherwise the lambda method is an instance method (on interfaces this is called "default") Where we are is: (1) Implemented except for default methods, there is a VM bug that requires us to make them public for now. (2) Implemented. (3) Implemented. -Robert > >> >> Maurizio > > R?mi > >> >> On 22/05/13 08:45, Remi Forax wrote: >>> The compiler doesn't like code like this: >>> >>> public interface Foo { >>> default void bar() { >>> Runnable r = () -> { >>> System.out.println(this); >>> }; >>> } >>> } >>> >>> in that case the lambda in bar can be compiled to a static method. >>> >>> But what if the lambda calls a super method like this: >>> interface I { >>> default void m() { } >>> } >>> >>> public interface Foo extends I{ >>> default void bar() { >>> Runnable r = () -> { >>> I.super.m(); >>> }; >>> } >>> } >>> >>> given that I.super.m() is translated to an invokespecial call and that >>> as far as I know invokespecial needs to be called in an instance >>> method. >>> >>> One possible translation is to translate the lambda to a default method >>> but given that a default method is public, an interface that extends >>> the interface may be able to override the code of the lambda >>> with another lambda declared in the class with the same signature >>> even if the two lambdas are unrelated. >>> >>> does it means that the lambda in interfaces must be numbered knowing >>> the whole hierarchy ? >>> >>> cheers, >>> R?mi >>> >> > From forax at univ-mlv.fr Wed May 22 11:51:25 2013 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 22 May 2013 20:51:25 +0200 Subject: Compilation of lambda in an interface, do we have a plan ? In-Reply-To: <519D0E29.20000@oracle.com> References: <519C77A1.6090702@univ-mlv.fr> <519C9E1A.3060201@oracle.com> <519CC83B.5020901@univ-mlv.fr> <519D0E29.20000@oracle.com> Message-ID: <519D13AD.5020409@univ-mlv.fr> Hi Robert, On 05/22/2013 08:27 PM, Robert Field wrote: > On 5/22/13 6:29 AM, Remi Forax wrote: >> On 05/22/2013 12:29 PM, Maurizio Cimadamore wrote: >>> I'm not sure as to why this hasn't been fixed by Robert recent >>> changes - Robert? >> >> It seems that Robert as done it's change on jdk8/tl/langtools and not >> lambda/lambda/langtools, >> so the version of javac from jdk8/tl is more recent that the one from >> lambda/lambda/langtools. > > Making interface lambda methods static where possible was done in > lambda weeks back. It depends what you mean by 'where possible', i.e. where you draw the line. As far as I understand, we only need to make the lambdas that do a super non-static, not all lambdas that capture 'this'. I'm not very comfortable to have instance default method in interface when a static method can work (even if it's not very rational, private methods are no more overridable that static ones). > > There are some other changes which have not been sync'ed back to lambda. >> >>> >>> I believe the plan is to go static for the first example, and >>> default _private_ for the second. >> >> Ok, >> I think that in that case lambda in class should be translated the >> same way. >> Currently, a lambda that capture this but doesn't use super is not >> translated to a static method. > > The plan is > (1) for all lambda methods to be private. > (2) where there is no implicit/explicit this reference, the lambda > method is static. > (3) otherwise the lambda method is an instance method (on interfaces > this is called "default") > > Where we are is: > (1) Implemented except for default methods, there is a VM bug that > requires us to make them public for now. > (2) Implemented. > (3) Implemented. Ok. > > -Robert cheers, R?mi > > >> >>> >>> Maurizio >> >> R?mi >> >>> >>> On 22/05/13 08:45, Remi Forax wrote: >>>> The compiler doesn't like code like this: >>>> >>>> public interface Foo { >>>> default void bar() { >>>> Runnable r = () -> { >>>> System.out.println(this); >>>> }; >>>> } >>>> } >>>> >>>> in that case the lambda in bar can be compiled to a static method. >>>> >>>> But what if the lambda calls a super method like this: >>>> interface I { >>>> default void m() { } >>>> } >>>> >>>> public interface Foo extends I{ >>>> default void bar() { >>>> Runnable r = () -> { >>>> I.super.m(); >>>> }; >>>> } >>>> } >>>> >>>> given that I.super.m() is translated to an invokespecial call and that >>>> as far as I know invokespecial needs to be called in an instance >>>> method. >>>> >>>> One possible translation is to translate the lambda to a default >>>> method >>>> but given that a default method is public, an interface that extends >>>> the interface may be able to override the code of the lambda >>>> with another lambda declared in the class with the same signature >>>> even if the two lambdas are unrelated. >>>> >>>> does it means that the lambda in interfaces must be numbered knowing >>>> the whole hierarchy ? >>>> >>>> cheers, >>>> R?mi >>>> >>> >> > From brian.goetz at oracle.com Wed May 22 12:05:43 2013 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 22 May 2013 15:05:43 -0400 Subject: Compilation of lambda in an interface, do we have a plan ? In-Reply-To: <519D13AD.5020409@univ-mlv.fr> References: <519C77A1.6090702@univ-mlv.fr> <519C9E1A.3060201@oracle.com> <519CC83B.5020901@univ-mlv.fr> <519D0E29.20000@oracle.com> <519D13AD.5020409@univ-mlv.fr> Message-ID: <519D1707.6090100@oracle.com> >> Making interface lambda methods static where possible was done in >> lambda weeks back. > > It depends what you mean by 'where possible', i.e. where you draw the line. > As far as I understand, we only need to make the lambdas that do a super > non-static, > not all lambdas that capture 'this'. > > I'm not very comfortable to have instance default method in interface > when a static method can work > (even if it's not very rational, private methods are no more overridable > that static ones). The eventual (for Java SE 8) outcome will be that we do the same thing in classes and interfaces. But there were some implementation difficulties that have slowed our movement in this direction, and they're being ironed out. From robert.field at oracle.com Wed May 22 13:49:26 2013 From: robert.field at oracle.com (Robert Field) Date: Wed, 22 May 2013 13:49:26 -0700 Subject: Compilation of lambda in an interface, do we have a plan ? In-Reply-To: <519D1707.6090100@oracle.com> References: <519C77A1.6090702@univ-mlv.fr> <519C9E1A.3060201@oracle.com> <519CC83B.5020901@univ-mlv.fr> <519D0E29.20000@oracle.com> <519D13AD.5020409@univ-mlv.fr> <519D1707.6090100@oracle.com> Message-ID: <519D2F56.3050609@oracle.com> On 05/22/13 12:05, Brian Goetz wrote: >>> Making interface lambda methods static where possible was done in >>> lambda weeks back. >> >> It depends what you mean by 'where possible', i.e. where you draw the >> line. >> As far as I understand, we only need to make the lambdas that do a super >> non-static, >> not all lambdas that capture 'this'. >> >> I'm not very comfortable to have instance default method in interface >> when a static method can work >> (even if it's not very rational, private methods are no more overridable >> that static ones). > > The eventual (for Java SE 8) outcome will be that we do the same thing > in classes and interfaces. But there were some implementation > difficulties that have slowed our movement in this direction, and > they're being ironed out. > We do the same thing in classes and interfaces now (save for the public default method work-around). Same logic. What Remi is saying is that using "this" doesn't NECCESSARILY require one to make it an instance method, we could translate the "this" and add a parameter to the static. Same with accessable references to instance methods/fields, with a little more complex translation. Note there can be inner classes, and .... In each case the complexity goes up, and we always have to make some of them instance methods. As we try to in general, we took the simpler route, in cases where the "best" route can still be implemented in the future. Given that the instance methods (including defaults) WILL be private. What is the source of your discomfort? -Robert