From archie.cobbs at gmail.com Sun Jun 2 20:05:03 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Sun, 2 Jun 2024 15:05:03 -0500 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> Message-ID: On Thu, May 30, 2024 at 5:55?PM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > That said, I'm not sure how common of a pattern this really is. All the > examples I've seen so far have a certain artificial vibe to them. The kind > of stuff compiler writers like us might want to write in a test :-) > > It would be helpful I think to run some analysis and get some numbers. > I'll see if I can do that, I think I know where to look. > I'm definitely interested in what you find. This is purely anecdotal, but in my experience *some* developers seem to be very fond of nested classes (where "fond of" might be a euphemism here). And of course those developers expect to be able to reference all of their "outer stuff" from wherever. They treat "cannot reference x before superclass constructor" errors as an annoying irritation to be worked around if possible. Hopefully JEP 482 will make some of their various gymnastics less necessary, not more. So I guess I'm saying nested classes in an early construction context that reference "outer stuff" may not be the most elegant way to code a solution in many cases, but I've seen it done that way. Of course in any case real data would be more enlightening. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From stephan.herrmann at berlin.de Sun Jun 2 21:36:03 2024 From: stephan.herrmann at berlin.de (Stephan Herrmann) Date: Sun, 2 Jun 2024 23:36:03 +0200 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> Message-ID: <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> Am 31.05.24 um 00:55 schrieb Maurizio Cimadamore: > > [...] > > That said, I'm not sure how common of a pattern this really is. All the examples > I've seen so far have a certain artificial vibe to them. The kind of stuff > compiler writers like us might want to write in a test :-) that's our job, isn't it? :) > It would be helpful I think to run some analysis and get some numbers. I'll see > if I can do that, I think I know where to look. Do you see a problem in the rules as explained by Dan? From what I understood so far those look fine to me (given his explanation :) ). If we can draw a clean and precise line between safe and unsafe code, why should we care about probabilities of people using the capabilities to the last dot? Do you consider the proposed rules too complex to implement cleanly, or too complex to explain to users? Defining early construction contexts relative to specific enclosing classes makes a lot of sense, if you ask me. Still, as long as we haven't implemented this change I might be missing some murky detail. thanks, Stephan From gavin.bierman at oracle.com Mon Jun 3 13:06:39 2024 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 3 Jun 2024 13:06:39 +0000 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> Message-ID: <8667CCBB-888A-4FF0-AACE-78EECD3F012F@oracle.com> [Back after a few days off?] Indeed, Archie and I looked at this, and some other approaches, but in the end we went for the simpler approach. Simpler seemed better - alternatives just seemed to make the spec as obscure as the corner-cases we were trying to address! Anyway, apologies as I didn?t document this explicitly in the spec, which would have been helpful. I will do so, and perhaps add some other editorial notes to make it clearer what we have done. Thanks, Gavin On 30 May 2024, at 23:16, Archie Cobbs wrote: On Thu, May 30, 2024 at 5:08?AM Maurizio Cimadamore > wrote: I note that this is effectively equivalent to say that the enclosing instance of the local class is the first enclosing instance that does not appear in a pre-construction context, correct? If we stated that, once and for all (in 8.1.3), couldn't we then avoid the need for having other special rules? (When trying to play with the JLS I still feel like a child wielding a weapon that's way too heavy for me) At one point I took a stab at what ?15.9.2 might say if you went down that route. It started with something like this: For any expression or class declaration E, define the first available enclosing instance of E, if any, as follows: o If E occurs in a static context, then there is no first available enclosing instance of E. o If E is a top-level class declaration, a static class declaration, or an interface declaration, then there is no first available enclosing instance of E. o Otherwise, let O be the immediately enclosing class declaration of E. o If E occurs in an early construction context of O, then the first available enclosing instance of E is the first available enclosing instance of O, if any. o Otherwise, the first available enclosing instance of E is O's instance of this. What's new here is that the definition is recursive, which seems to be inescapable if you want the rules to match the compiler's actual behavior. But I agree with Dan that it's simpler to avoid all that and just say when you can and cannot reference the enclosing instances, i.e., remember we only need to specify the language here, not the compilation process. This works for local and anonymous classes because the entire class, and therefore all references to an enclosing instance from the class, are either in a pre-construction context or not, and so there's never a difference between "the enclosing instance X exists" and "the expression X.this is legal". Of course member classes are different - the enclosing instance is provided either explicitly or implicitly at construction time, and separate rules apply to handle that. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Mon Jun 3 13:06:39 2024 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 3 Jun 2024 13:06:39 +0000 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> Message-ID: <8667CCBB-888A-4FF0-AACE-78EECD3F012F@oracle.com> [Back after a few days off?] Indeed, Archie and I looked at this, and some other approaches, but in the end we went for the simpler approach. Simpler seemed better - alternatives just seemed to make the spec as obscure as the corner-cases we were trying to address! Anyway, apologies as I didn?t document this explicitly in the spec, which would have been helpful. I will do so, and perhaps add some other editorial notes to make it clearer what we have done. Thanks, Gavin On 30 May 2024, at 23:16, Archie Cobbs wrote: On Thu, May 30, 2024 at 5:08?AM Maurizio Cimadamore > wrote: I note that this is effectively equivalent to say that the enclosing instance of the local class is the first enclosing instance that does not appear in a pre-construction context, correct? If we stated that, once and for all (in 8.1.3), couldn't we then avoid the need for having other special rules? (When trying to play with the JLS I still feel like a child wielding a weapon that's way too heavy for me) At one point I took a stab at what ?15.9.2 might say if you went down that route. It started with something like this: For any expression or class declaration E, define the first available enclosing instance of E, if any, as follows: o If E occurs in a static context, then there is no first available enclosing instance of E. o If E is a top-level class declaration, a static class declaration, or an interface declaration, then there is no first available enclosing instance of E. o Otherwise, let O be the immediately enclosing class declaration of E. o If E occurs in an early construction context of O, then the first available enclosing instance of E is the first available enclosing instance of O, if any. o Otherwise, the first available enclosing instance of E is O's instance of this. What's new here is that the definition is recursive, which seems to be inescapable if you want the rules to match the compiler's actual behavior. But I agree with Dan that it's simpler to avoid all that and just say when you can and cannot reference the enclosing instances, i.e., remember we only need to specify the language here, not the compilation process. This works for local and anonymous classes because the entire class, and therefore all references to an enclosing instance from the class, are either in a pre-construction context or not, and so there's never a difference between "the enclosing instance X exists" and "the expression X.this is legal". Of course member classes are different - the enclosing instance is provided either explicitly or implicitly at construction time, and separate rules apply to handle that. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Fri Jun 7 21:23:41 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Fri, 7 Jun 2024 22:23:41 +0100 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> Message-ID: <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> On 02/06/2024 22:36, Stephan Herrmann wrote: > Do you consider the proposed rules too complex to implement cleanly, > or too complex to explain to users? It's a decent rule and explanation of what the compiler does. The more I play with code like this, the more I find examples where, on a first look is not immediately clear which way the compiler should go (it's kind of hard to mentally keep track of which classes have members that can be accessed), or how they should be translated. It probably doesn't help that the implementation seems to be all over the place in this area. So, my argument is mostly a pedagogical one, e.g. drawing a clear line that is less prone to mis-interpretation and might make the code slightly more readable and predictable. Maurizio From archie.cobbs at gmail.com Fri Jun 7 22:48:15 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Fri, 7 Jun 2024 17:48:15 -0500 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> Message-ID: I think one thing we could do to help lower the confusion level is come up with some less ambiguous "enclosing" terminology. For example, the term "enclosing instance" is ambiguous. Consider this class: $ cat E1.java class E1 { E1() { class E2 { E2() { class E3 { void run() { System.out.println(E1.this); // ok //System.out.println(E2.this); // error System.out.println(E3.this); // ok } } new E3().run(); super(); } } new E2(); } public static void main(String[] args) { new E1(); } } $ javac --enable-preview --release 24 E1.java && java --enable-preview -classpath classes E1 E1 at 6537cf78 E1$1E2$1E3 at 3cbbc1e0 Does class E3 have an "enclosing instance"? It's a trick question.... - If "yes", then why is E2.this inaccessible? - If "no", then why is E1.this accessible? The compiler's "NOOUTERTHIS" doesn't help. It doesn't mean there's no outer 'this' instance, it just means there's no *immediate* outer 'this' instance.. and the "outerThisStack" is the stack of outer instances you get when you skip over the NOOUTERTHIS ones (I think). Regardless, when looking at the compiler code, there's many mentions of "enclosing instances" and "outer instances" but it's easy to have the wrong assumption in your head about what these terms actually mean. In an ideal world we would all agree to use some standard, non-ambiguous terminology, e.g.: - The phrasing should always be "enclosing instance with respect to D", where D is some lexically enclosing class. - For a class C, the "enclosing instance of C with respect to D" is *defined* if: - D is a class (not interface, record, enum, etc.) - C is nested within D - There is no static class (or interface, record, enum, etc.) declaration between C and D (including C itself) - For a class C, the "enclosing instance of C with respect to D" is *accessible* at some point X in C if: - The enclosing instance of C with respect to D is defined - X is not in an early construction context of D - If C is nested directly within D with no intermediate classes, then "enclosing instance of C with respect to D" is also "the immediately enclosing instance of C". -Archie On Fri, Jun 7, 2024 at 5:13?PM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > On 02/06/2024 22:36, Stephan Herrmann wrote: > > Do you consider the proposed rules too complex to implement cleanly, > > or too complex to explain to users? > > It's a decent rule and explanation of what the compiler does. -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From stephan.herrmann at berlin.de Sun Jun 9 08:57:27 2024 From: stephan.herrmann at berlin.de (Stephan Herrmann) Date: Sun, 9 Jun 2024 10:57:27 +0200 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> Message-ID: <8b1dd200-7783-4502-ab1b-1ea0972704d6@berlin.de> Am 08.06.24 um 00:48 schrieb Archie Cobbs: > I think one thing we could do to help lower the confusion level is come up with > some less ambiguous "enclosing" terminology. sounds good to me > The compiler's "NOOUTERTHIS" doesn't help. I have no idea what "NOOUTERTHIS" means in javac. ecj does not have any such feature and doesn't need it, AFAICS. > In an ideal world we would all agree to use some standard, non-ambiguous > terminology, e.g.: > > * The phrasing should always be "enclosing instance with respect to D", where > D is some lexically enclosing class. > * For a class C, the "enclosing instance of C with respect to D" is /defined/ if: > o D is a class (not interface, record, enum, etc.) > o C is nested within D > o There is no static class (or interface, record, enum, etc.) declaration > between C and D (including C itself) > * For a class C, the "enclosing instance of C with respect to D" is > /accessible/ at some point X in C if: > o The enclosing instance of C with respect to D is defined > o X is not in an early construction context of D > * If C is nested directly within D with no intermediate classes, then > "enclosing instance of C with respect to D" is also "the immediately > enclosing instance of C". Looks good to me, but do we actually need to speak about enclosing instances to determine legality of expressions? The context dependence already shows up in sentences like: - "If the expression name appears in an early construction context of C" (6.5.6.1) or = "It is a compile-time error if a qualified this expression occurs in an early construction context (8.8.7.1) of the class named by TypeName." (15.8.4). Anyway, decoupling *definition* of enclosing instances from the question whether they can be "used" in certain contexts looks like a winner to me (not sure if "accessible" is the best term here, which otherwise relates to encapsulation). regards, Stephan From archie.cobbs at gmail.com Sun Jun 9 18:57:11 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Sun, 9 Jun 2024 13:57:11 -0500 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: <8b1dd200-7783-4502-ab1b-1ea0972704d6@berlin.de> References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <8b1dd200-7783-4502-ab1b-1ea0972704d6@berlin.de> Message-ID: On Sun, Jun 9, 2024 at 3:57?AM Stephan Herrmann wrote: > Am 08.06.24 um 00:48 schrieb Archie Cobbs: > > I think one thing we could do to help lower the confusion level is come > up with > > some less ambiguous "enclosing" terminology. > > sounds good to me > > > The compiler's "NOOUTERTHIS" doesn't help. > > I have no idea what "NOOUTERTHIS" means in javac. > My apologies, I'm mixing together spec issues and javac issues - which is probably not appropriate for this particular list. >From a pure spec point of view, I think things are in reasonably good shape. What's needed may be some more precision/clarity in the openjdk javac code (comments, etc.), and of course that documentation should utilize verbiage defined in the JLS. But I should save those opinions for compiler-dev :) -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Mon Jun 10 09:24:15 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Mon, 10 Jun 2024 10:24:15 +0100 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> Message-ID: Hi Archie, your comment captures exactly the kind of things I'm worried about. We basically have classes which have no enclosing instances, but where enclosing members can be accessed (via other means). The solution is, for the JLS to define what's accessible and what's not, in any given scope. But this leaves javac (and other compilers) to wrestle with the specifics on how this is actually translated, and, due to the non-uniformity, the complexity explodes in our face. Historically, the JLS used to treat this/super calls as a static context (8.8.7.1). And then JLS also used to say that inner classes defined in a static context had no enclosing instance (8.1.3). So, for this particular JEP, I think there's a choice in front of us: * do we keep JLS as is (and fix javac to do what the spec has always said the behavior was) ? * or, do we keep javac as is and then tweak the JLS to model what the implemented behavior is? The latter path seems to have been chosen. Do we have a feeling that local classes in pre-construction context will be radically more common than they are today? Or are there other reasons behind this decision? Perhaps the tacit understanding was that "javac got this right", but looking at the increasing numbers of bugs filed recently in this area, and after looking more at how the code works, it seems that javac doesn't have a very principled way to get there, and it is in fact rather easy to come up with examples which defeats javac's translation strategy. > Regardless, when looking at the compiler code, there's many mentions > of "enclosing instances" and "outer instances" but it's easy to have > the wrong assumption in your head about what these terms actually mean. > > In an ideal world we would all agree to use some standard, > non-ambiguous terminology, e.g.: > > * The phrasing should always be "enclosing instance with respect to > D", where D is some lexically enclosing class. > * For a class C, the "enclosing instance of C with respect to D" is > /defined/ if: > o D is a class (not interface, record, enum, etc.) > o C is nested within D > o There is no static class (or interface, record, enum, etc.) > declaration between C and D (including C itself) > * For a class C, the "enclosing instance of C with respect to D" is > /accessible/ at some point X in C if: > o The enclosing instance of C with respect to D is defined > o X is not in an early construction context of D > * If C is nested directly within D with no intermediate classes, > then "enclosing instance of C with respect to D" is also "the > immediately enclosing instance of C". > > I agree this is confusing and we need better terminology. But I'm having trouble with the model that A is an enclosing instance of B, but A's members are not accessible in B. I understand why the spec does this - but it would be vastly simpler (at least from an implementation perspective) then to say that A is not in fact an enclosing instance of B, but that B still has some other enclosing instance (some enclosing class of A). A model such as this would inform the translation strategy quite clearly. In fact, I'm starting to think if we really want to go down the path of non-static local classes in pre-construction contexts, then javac should do things this way regardless of how things are specified, as that will make the translation strategy more uniform and less buggy than the one we have today. Maurizio -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Mon Jun 10 16:32:32 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Mon, 10 Jun 2024 11:32:32 -0500 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> Message-ID: On Mon, Jun 10, 2024 at 4:24?AM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > We basically have classes which have no enclosing instances, but where > enclosing members can be accessed (via other means). > Hah, you've already got me in a screeching halt on the first sentence here, because I'm inferring a different meaning of "enclosing instance" than what I knew (and you also throw in "enclosing members", phew :) Just so we're on the same page - with respect to the anonymous Runnable class below, how would you describe "ClassA.this" and "ClassB.this"? class ClassA { class ClassB { ClassB() { this(new Runnable() { public void run() { ClassA.this.hashCode(); } }); } ClassB(Runnable r) { } } } Historically, the JLS used to treat this/super calls as a static context > (8.8.7.1). > That's what the JLS said, but of course that's not what the compiler ever implemented (JDK-8301649). > And then JLS also used to say that inner classes defined in a static > context had no enclosing instance (8.1.3). > > So, for this particular JEP, I think there's a choice in front of us: > > * do we keep JLS as is (and fix javac to do what the spec has always said > the behavior was) ? > * or, do we keep javac as is and then tweak the JLS to model what the > implemented behavior is? > > The latter path seems to have been chosen. Do we have a feeling that local > classes in pre-construction context will be radically more common than they > are today? Or are there other reasons behind this decision? > Here is my own understanding of the reasoning behind choosing option #2 (others may differ). First of all, the question of how to treat classes in early construction contexts is not new - it's just become a lot more obvious because of the new flexibility offered in the JEP. The "early construction context" already exists in constructor parameters, although of course more shoe-horning required doing it the old way. For example this code compiles fine in JDK 17: class EarlyLocal { int x; class Inner { Inner(int x) { this(switch (x) { default -> { class Local { { EarlyLocal.this.hashCode(); } } yield 0.0; } }); } Inner(double x) { } } } So that means there is some amount of code out there (unknown how much) which is, for example, referencing 2nd outer instances from anonymous classes that are created inside constructor parameters (a more likely example of that is the one given earlier in this email thread (May 30th, "class Outer")). A second reason is that access to 2nd & further outer instances is something developers understand and expect, and denying it to them for no particularly good reason (my opinion) in one place (early construction) but not anywhere else will strike them as arbitrarily inconsistent and annoying. Putting this another way, developers already have an intuitive understanding that you can't access 'this' prior to superclass construction, so that exception is already baked into their lives. If we make early construction contexts truly static (option #1), then we're creating yet another exception they will have to learn and work around. Contrast with option #2 which just extends everything they already know and expect in a natural way. > Perhaps the tacit understanding was that "javac got this right", but > looking at the increasing numbers of bugs filed recently in this area, and > after looking more at how the code works, it seems that javac doesn't have > a very principled way to get there, and it is in fact rather easy to come > up with examples which defeats javac's translation strategy. > I agree that the increasing number of bugs is alarming. But I attribute this to poor internal documentation and years of early construction fixes. If you look back at Jira historically you'll see a lot of bugs that relate specifically to early construction context issues (in constructor parameters, obviously). The NOOUTERTHIS flag is an example of how these got fixed with (arguably) a quick hack rather than a more principled refactoring. IMHO, saying that these are reasons to downgrade the spec is the tail wagging the dog. I agree this is confusing and we need better terminology. But I'm having > trouble with the model that A is an enclosing instance of B, but A's > members are not accessible in B. I understand why the spec does this - but > it would be vastly simpler (at least from an implementation perspective) > then to say that A is not in fact an enclosing instance of B, but that B > still has some other enclosing instance (some enclosing class of A). A > model such as this would inform the translation strategy quite clearly. In > fact, I'm starting to think if we really want to go down the path of > non-static local classes in pre-construction contexts, then javac should do > things this way regardless of how things are specified, as that will make > the translation strategy more uniform and less buggy than the one we have > today. > This makes plenty of sense to me and I'd be happy with it. Frankly I don't care what the terminology is, as long as it is precise/unambiguous and universally agreed-to. We all need to be speaking the same language (and that includes internal compiler method names, variable names, and comments - with the caveat that this part is an openjdk/javac discussion probably more appropriate for compiler-dev). -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Mon Jun 10 17:10:05 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Mon, 10 Jun 2024 18:10:05 +0100 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> Message-ID: <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> On 10/06/2024 17:32, Archie Cobbs wrote: > On Mon, Jun 10, 2024 at 4:24?AM Maurizio Cimadamore > wrote: > > We basically have classes which have no enclosing instances, but > where enclosing members can be accessed (via other means). > > > Hah, you've already got me in a screeching halt on the first sentence > here, because I'm inferring a different meaning of "enclosing > instance" than what I knew (and you also throw in "enclosing members", > phew :) > > Just so we're on the same page - with respect to the anonymous > Runnable class below, how would you describe "ClassA.this" and > "ClassB.this"? The anon Runnable has two enclosing instances. One of type ClassB, one of type ClassA. The former cannot be accessed, the latter can. > > class ClassA { > ? ? class ClassB { > ? ? ? ? ClassB() { > ? ? ? ? ? ? this(new Runnable() { > ? ? ? ? ? ? ? ? public void run() { > ? ? ? ? ? ? ? ? ? ? ClassA.this.hashCode(); > ? ? ? ? ? ? ? ? } > ? ? ? ? ? ? }); > ? ? ? ? } > ? ? ? ? ClassB(Runnable r) { > ? ? ? ? } > ? ? } > } > > > Historically, the JLS used to treat this/super calls as a static > context (8.8.7.1). > > > That's what the JLS said, but of course that's not what the compiler > ever implemented (JDK-8301649). Sure. What about ecj? What did that compiler implement? (This is more a question for Stephan). What I?m trying to say is that ?because javac does it? is not always a good reason for doing something. In this particular case, the contrived nature of the examples involved, and the complexity of the resulting translation scheme make me question as to what the right choice should be. > And then JLS also used to say that inner classes defined in a > static context had no enclosing instance (8.1.3). > > So, for this particular JEP, I think there's a choice in front of us: > > * do we keep JLS as is (and fix javac to do what the spec has > always said the behavior was) ? > * or, do we keep javac as is and then tweak the JLS to model what > the implemented behavior is? > > The latter path seems to have been chosen. Do we have a feeling > that local classes in pre-construction context will be radically > more common than they are today? Or are there other reasons behind > this decision? > > Here is my own understanding of the reasoning behind choosing option > #2 (others may differ). > > First of all, the question of how to treat classes in early > construction contexts is not new - it's just become a lot more obvious > because of the new flexibility offered in the JEP. > The "early construction context" already exists in constructor > parameters, although of course more shoe-horning required doing it the > old way. > > For example this code compiles fine in JDK 17: > > class EarlyLocal { > ? ? int x; > ? ? class Inner { > ? ? ? ? Inner(int x) { > ? ? ? ? ? ? this(switch (x) { > ? ? ? ? ? ? ? ? default -> { > ? ? ? ? ? ? ? ? ? ? class Local { { EarlyLocal.this.hashCode(); } } > ? ? ? ? ? ? ? ? ? ? yield 0.0; > ? ? ? ? ? ? ? ? } > ? ? ? ? ? ? }); > ? ? ? ? } > ? ? ? ? Inner(double x) { > ? ? ? ? } > ? ? } > } > > So that means there is some amount of code out there (unknown how > much) which is, for example, referencing 2nd outer instances from > anonymous classes that are created inside constructor parameters (a > more likely example of that is the one given earlier in this email > thread (May 30th, "class Outer")). This means ?there might be? some amont of code. Some initial analysis against Maven found in fact /no uses/. But I will need to double check the results, and also include lambda expressions in the mix. My beef with this thread is that we keep using the fact that it ?works in javac? as a proof that the feature has to be designed this way. Here?s a recently discovered counter-example: |class Outer { interface A { } interface B { } class Inner1 { Inner1() { this(new A() { class Inner2 { Inner2() { this(new B() { { m(); g(); } }); } Inner2(Object o) { } } void m() { } }); } Inner1(Object o) { } } void g() { } } | This does NOT work with javac, in fact it never did (I went back as far back as Java 7, but I have no reason to believe it ever worked). This example defeats the very assumption javac relies upon when translating local classes - that there is a chain of enclosing instances, reachable using some hidden field. In this case there?s /two/ enclosing instances, and one is not reachable by the other. We can fix this by making the implementation even more complex. Or we can step back, and ask ourselves: how many developers, when seeing the above code, will immediately grasp what that code means? My bet is that nobody /really/ wants to write code that looks like this. Surely nobody wants to read it (I don?t!). > > A second reason is that access to 2nd & further outer instances is > something developers understand and expect, and denying it to them for > no particularly good reason (my opinion) in one place (early > construction) but not anywhere else will strike them as arbitrarily > inconsistent and annoying. Putting this another way, developers > already have an intuitive understanding that you can't access 'this' > prior to superclass construction, so that exception is already baked > into their lives. If we make early construction contexts truly static > (option #1), then we're creating yet another exception they will have > to learn and work around. Contrast with option #2 which just extends > everything they already know and expect in a natural way. The example above has nothing intuitive about it IMHO. The problem is that we?re saying, /simultaneously/ that: * it is an error to use |this| in a pre-construction context * it is ok for a local class in a pre-construction context to use enclosing instances other than ?problematic ones? This is IMHO what leads to non-intuitiveness. I claim that the mental model developers have about encloising instances is that either there is an innermost enclosing instance, or there?s none. And if there is one, all outer enclosing instances are reachable, somehow, via the innermost instance. The model we?re proposing in #2 is different, and amounts at saying that a class can have N enclosing instances, all potentially unrelated from each other. So, if A contains B, and B contains C, we can end up with cases where C can access the enclosing instance for A, whereas B cannot. This seems to violate a natural, intuitive, and compositional behavior (e.g. if both C and B have enclosing instances, then ?surely? A must be the type an enclosing instance with respect to both). > Perhaps the tacit understanding was that "javac got this right", > but looking at the increasing numbers of bugs filed recently in > this area, and after looking more at how the code works, it seems > that javac doesn't have a very principled way to get there, and it > is in fact rather easy to come up with examples which defeats > javac's translation strategy. > > I agree that the increasing number of bugs is alarming. But I > attribute this to poor internal documentation and years of early > construction fixes. If you look back at Jira historically you'll see a > lot of bugs that relate specifically to early construction context > issues (in constructor parameters, obviously). The NOOUTERTHIS flag is > an example of how these got fixed with (arguably) a quick hack rather > than a more principled refactoring. IMHO, saying that these are > reasons to downgrade the spec is the tail wagging the dog. I?m not saying we shouldn?t do it because it?s hard to implement. I?m saying that the model put forward by the spec is (a) not very intuitive (despite what you seem to claim) and (b) not a very useful generalization. My point here is that I dare to find a developer that in 2 seconds will be able to determine whether the code I wrote above should compile or not. And if developers should spend 10 minutes chasing down the spec to understand what the program is supposed to mean, are we sure we want them to write code like this in the first place? So, while we can spend resources in chasing down every single issue in this area, this feels, I admit, not a great use of our collective time (which I?m sure is limited). Maurizio > > > -Archie > > -- > Archie L. Cobbs ? -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Mon Jun 10 18:21:43 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Mon, 10 Jun 2024 13:21:43 -0500 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> Message-ID: On Mon, Jun 10, 2024 at 12:10?PM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > I?m saying that the model put forward by the spec is (a) not very > intuitive (despite what you seem to claim) and (b) not a very useful > generalization. > I agree with you that it's totally debatable what is most "intuitive" and "useful" because those are opiniony words. So I don't have an ironclad counter-argument. I also agree that your complicated example is (a) unrealistic and (b) demonstrates that the compiler still has bugs in this area. The strongest argument I have for preserving the current behavior is that the overall Java/JDK project has been traditionally very careful to preserve backward compatibility. Changing the compiler so that code in the world that has compiled successfully since Java 8 no longer compiles is not something to be taken lightly. In fact the compiler itself has a few examples, e.g., TypeEnter.java line 301 - the constructor TypeEnter.ImportsPhase() invokes super(..., new TypeEnter.HierarchyPhase()) which wouldn't be possible if early construction contexts were treated as static (i.e., no access to enclosing instances). There may be some better, less draconian restriction than "treated as static" but I'm not sure it would be appreciably more intuitive than what we've currently got, though I'm curious to hear your ideas. To put it another way, I think things only get non-intuitive with the current proposal for contrived examples. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Mon Jun 10 20:36:59 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Mon, 10 Jun 2024 21:36:59 +0100 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> Message-ID: <3f0c6d7e-b57e-4f49-aa0a-4ee7c58dba38@oracle.com> On 10/06/2024 19:21, Archie Cobbs wrote: > In fact the compiler itself has a few examples, e.g., TypeEnter.java > line 301 - the constructor TypeEnter.ImportsPhase() invokes super(..., > new TypeEnter.HierarchyPhase()) which wouldn't be possible if early > construction contexts were treated as static (i.e., no access to > enclosing instances). I get your "slippery slope argument". The example you mention is like this: ``` class Outer { ?? class A { ??????? A() { this(new B()); } ??????? ... ?? } ?? class B { ... } } ``` Both A and B are member inner classes, and they have an enclosing instance of type Outer. This enclosing instance is what's used to create a new B() from within the constructor of A. Note that in this case we're not really talking of a class declaration occurring in a pre-construction context, so we don't really have to decide anything when it comes to enclosing instances. This example boils doing to deciding whether "Outer.this" is valid from the pre-construction context of A. And the answer is yes, because A has an enclosing instance of type Outer. So, what I suggested (treat all class declarations in pre-construction context as having no enclosing instance) would really not change any of this. But I suspect what you meant (but you didn't say) is that this would feel inconsistent - e.g. `new B()` is ok. But if you have a local class declaration and the declaration captures Outer.this, that would not be ok if the local decl is static. If this is what you meant, I get it - although not too sure what to make of it. In the sense that it's not super clear whether these two examples should behave in exactly the same way (given one doesn't introduce a _new_ class declaration). I guess for now I'd be very interested in learning how compilers (other than javac) deal with some of the cases we discussed in this thread (if they do at all, given javac's behavior is currently outside JLS). Maurizio From archie.cobbs at gmail.com Mon Jun 10 22:40:55 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Mon, 10 Jun 2024 17:40:55 -0500 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: <3f0c6d7e-b57e-4f49-aa0a-4ee7c58dba38@oracle.com> References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> <3f0c6d7e-b57e-4f49-aa0a-4ee7c58dba38@oracle.com> Message-ID: On Mon, Jun 10, 2024 at 3:37?PM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > But I suspect what you meant (but you didn't say) is Thanks - you read my mind better than I do :) My example didn't really prove anything. Here's a better one. Attr.java line 617 refers to the instance field Attr.chk from anonymous inner class RecoveryInfo.Check.NestedCheckContext$1 which is declared in a super() parameter in the RecoveryInfo() constructor. If that anonymous inner class had no outer instances, then wouldn't it be unable to access Attr.this.chk ? In summarized form this example similar to others I've been showing: class Outer int x; class Inner extends Foo { Inner() { super(new Bar() { { Outer.this.x++; } // allowed??? }); } } } I want to make sure I'm not misunderstanding your proposal. When you say "treat all class declarations in pre-construction context as having no enclosing instance" I still read the phrase "enclosing instance" as potentially ambiguous (does that include "2nd enclosing instance"? Etc.). But previously you said: "Wouldn't it be easier to say that every class declared in a pre-construction context is static, period, and cannot reference anything from enclosing contexts?" so I'll assume that's what you mean. So the two proposals we're talking about for when an outer class instance expression "Foo.this" is invalid are: 1. "Foo.this" is invalid when the expression is contained in a Foo early construction context (with any level of nesting) (this is what the JEP proposes) 2. "Foo.this" is invalid when the expression is contained in *any* early construction context (your proposal?) 3. Is there some other hybrid proposal? I'm not going to argue that #1 is the best in all possible worlds, but I think #1 is better than #2, if only because #2 will cause real-world code out there to start failing to compile - including javac itself. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Tue Jun 11 08:53:23 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Tue, 11 Jun 2024 09:53:23 +0100 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> <3f0c6d7e-b57e-4f49-aa0a-4ee7c58dba38@oracle.com> Message-ID: <022bdbec-dfb0-4b65-96c0-797e316197aa@oracle.com> > > Here's a better one. Attr.java line 617 refers to the instance field > Attr.chk > > from anonymous inner class RecoveryInfo.Check.NestedCheckContext$1 > which is declared in a super() parameter in the RecoveryInfo() > constructor. > > If that anonymous inner class had no outer instances, then wouldn't it > be unable to access Attr.this.chk ? I know that example. In fact that?s the only example I could find that was similar to the cases we discussed. The JDK has other 8 class declarations in a pre-construction context, but they have /no available / enclosing instance, because are something like: |class Toplevel { Toplevel() { this(new Object() { ... }); } } | So, back to Attr - turns out that this code (of which I?m not very proud of :-) ), can be rewritted w/o accessing chk.basicHandler inside the anon class. In fact, we are passing chk.basicHandler to the anon constructor, and that value is made available through the inherited ?encl? field. So, if we replace |chk.basicHandler| with |encl|, this class becomes ?well-behaved? again. Which suggests a possible tactic for dealing with issues such as this: the class that creates the local anon class has access to the outer this, and can pass them as constructor arguments. > I want to make sure I'm not misunderstanding your proposal.? When you > say "treat all class declarations in pre-construction context as > having no enclosing instance" I still read the phrase "enclosing > instance" as potentially ambiguous (does that include "2nd enclosing > instance"? Etc.). > > But previously you said: "Wouldn't it be easier to say that every > class declared in a pre-construction context is static, period, and > cannot reference anything from enclosing contexts?" so I'll assume > that's what you mean. Yes, I meant that /no enclosing instance/ is available from a class declaration occurring in a pre-construction context. So your #2 below. > > So the two proposals we're talking about for when an outer class > instance expression "Foo.this" is invalid are: > > 1. "Foo.this" is invalid when the expression is contained in a Foo > early construction context (with any level of nesting) (this is > what the JEP proposes) > 2. "Foo.this" is invalid when the expression is contained in /any/ > early construction context (your proposal?) > 3. Is there some other hybrid proposal? > > I'm not going to argue that #1 is the best in all possible worlds, but > I think #1 is better than #2, if only because #2 will cause real-world > code out there to start failing to compile - including javac itself. > > I?m not worried much about the ?code out there? which doesn?t really seem to have taken up on such idioms. Thinking more, I think I can perhaps find an argument in defense of the strategy of the approach outlined in the current JEP: migrating lambdas to classes. So, back to your example: |class Outer int x; class Inner extends Foo { Inner() { super(new Bar() { { Outer.this.x++; } // allowed??? }); } } } | Let?s say we write this using lambdas: |class Outer int x; class Inner extends Foo { Inner() { super(() -> { ... Outer.this.x++ ... }); }); } } } | This is now ok, as the lambda has access to any enclosing instances that were available at the time the lambda was created (e.g. Outer.this). So, the best supporting argument I can find is that, by making the enclosing instance rules more precise, we can actually support a fuller migration between lambdas and local/anon classes. Now, such a migration is still not perfect, as lambdas and local classes have different meaning of |this| and |return|, so you still just can?t copy and paste code blindly and assume it will work. But, at least we wouldn?t be adding yet another asymmetry to the list. Maurizio ? -------------- next part -------------- An HTML attachment was scrubbed... URL: From stephan.herrmann at berlin.de Tue Jun 11 12:37:42 2024 From: stephan.herrmann at berlin.de (Stephan Herrmann) Date: Tue, 11 Jun 2024 14:37:42 +0200 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> Message-ID: Am 10.06.24 um 19:10 schrieb Maurizio Cimadamore: > On 10/06/2024 17:32, Archie Cobbs wrote: > >> On Mon, Jun 10, 2024 at 4:24?AM Maurizio Cimadamore >> [...] >> Historically, the JLS used to treat this/super calls as a static context >> (8.8.7.1). >> >> >> That's what the JLS said, but of course that's not what the compiler ever >> implemented (JDK-8301649). > > Sure. What about ecj? What did that compiler implement? (This is more a question > for Stephan). A quick sample shows that flags insideConstructorCall and insideStaticContext differ only in what exact error message ecj gives. So, generally ecj does implement that rule. Relevant code sections go back to some time around 2003. I see no reason to consider that rule as a failure. > What I?m trying to say is that ?because javac does it? is not always a good > reason for doing something. I couldn't agree more :) Am 10.06.24 um 11:24 schrieb Maurizio Cimadamore: > So, for this particular JEP, I think there's a choice in front of us: > > * do we keep JLS as is (and fix javac to do what the spec has always said the > behavior was) ? > * or, do we keep javac as is and then tweak the JLS to model what the > implemented behavior is? Which state of JLS are you referring to? Current stable version? Version as of JEP 447, latest draft for JEP 482? If stable version, how should early-construction context be integrated in your opinion? My current reading is: JEP 482 draft is consistent. I'm not aware of an alternative specification with the same effect concerning the core intentions of that JEP. If a consistent draft is provided, I'll be happy to review it. regards, Stephan From stephan.herrmann at berlin.de Tue Jun 11 13:05:00 2024 From: stephan.herrmann at berlin.de (Stephan Herrmann) Date: Tue, 11 Jun 2024 15:05:00 +0200 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> Message-ID: Am 07.06.24 um 23:23 schrieb Maurizio Cimadamore: > > On 02/06/2024 22:36, Stephan Herrmann wrote: >> Do you consider the proposed rules too complex to implement cleanly, or too >> complex to explain to users? > > It's a decent rule and explanation of what the compiler does. > > The more I play with code like this, the more I find examples where, on a first > look is not immediately clear which way the compiler should go (it's kind of > hard to mentally keep track of which classes have members that can be accessed), > or how they should be translated. It probably doesn't help that the > implementation seems to be all over the place in this area. > > So, my argument is mostly a pedagogical one, e.g. drawing a clear line that is > less prone to mis-interpretation and might make the code slightly more readable > and predictable. > > Maurizio Here's a different perspective that could also affect the pedagogical aspect: I've seen academic approaches that tackle the initialization issue by discriminating two variants of every reference type T: * type "raw T" captures instances that are not yet fully initialized * type "cooked T" is the type of instances with initialization completed (sorry for overloading 'raw' in this context, I only wish the distinction raw-parameterized could be retired completely :) ). So, within a constructor 'this' always has a "raw" type, which implies that the things you can do with that value are limited. (In a fully elaborated system this would even allow post-constructor initialization without risk of exposing uninitialized fields, or select which methods can be safely invoked on a "raw" this pointer -- not to mention the great benefit for NPE-analysis). Perhaps some years from now Java will even adopt such type modifiers, but already for the discussion at hand I'd think that the existence of any "Outer.this" can be determined purely based on lexical nesting (unless 'static' is said explicitly), but semantic analysis will know that some of these references will have a "raw type", imposing the necessary restrictions. Maybe existence of enclosing instance references is not boolean but three-valued: absent-raw-cooked. IOW, I see nothing awkward with enclosing instance references that impose some restrictions on their usage. OTOH the implementation pattern "this.this$0" does *not* require "this" to be fully cooked, right? best, Stephan From maurizio.cimadamore at oracle.com Tue Jun 11 14:05:03 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Tue, 11 Jun 2024 15:05:03 +0100 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> Message-ID: On 11/06/2024 13:37, Stephan Herrmann wrote: > Am 10.06.24 um 19:10 schrieb Maurizio Cimadamore: >> On 10/06/2024 17:32, Archie Cobbs wrote: >> >>> On Mon, Jun 10, 2024 at 4:24?AM Maurizio Cimadamore [...] >>> ??? Historically, the JLS used to treat this/super calls as a static >>> context >>> ??? (8.8.7.1). >>> >>> >>> That's what the JLS said, but of course that's not what the compiler >>> ever implemented (JDK-8301649). >> >> Sure. What about ecj? What did that compiler implement? (This is more >> a question for Stephan). > > A quick sample shows that flags insideConstructorCall and > insideStaticContext differ only in what exact error message ecj gives. > So, generally ecj does implement that rule. Relevant code sections go > back to some time around 2003. I see no reason to consider that rule > as a failure. So, to be clear, the Eclipse compiler rejects stuff like the one we?ve been discussing - e.g. |class Outer int x; class Inner extends Foo { Inner() { super(new Bar() { { Outer.this.x++; } // allowed??? }); } } } | Correct? > > >> What I?m trying to say is that ?because javac does it? is not always >> a good reason for doing something. > > I couldn't agree more :) > > > Am 10.06.24 um 11:24 schrieb Maurizio Cimadamore: > > So, for this particular JEP, I think there's a choice in front of us: > > > > * do we keep JLS as is (and fix javac to do what the spec has always > said the > > behavior was) ? > > * or, do we keep javac as is and then tweak the JLS to model what the > > implemented behavior is? > > Which state of JLS are you referring to? Current stable version? > Version as of JEP 447, latest draft for JEP 482? If stable version, > how should early-construction context be integrated in your opinion? I?m referring to JEP 482. Maurizio > > regards, > Stephan > > ? -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Tue Jun 11 14:13:29 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Tue, 11 Jun 2024 15:13:29 +0100 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> Message-ID: <1ef4baad-01f7-4c5a-9a70-93e18bbc7cea@oracle.com> On 11/06/2024 14:05, Stephan Herrmann wrote: > Am 07.06.24 um 23:23 schrieb Maurizio Cimadamore: >> >> On 02/06/2024 22:36, Stephan Herrmann wrote: >>> Do you consider the proposed rules too complex to implement cleanly, >>> or too complex to explain to users? >> >> It's a decent rule and explanation of what the compiler does. >> >> The more I play with code like this, the more I find examples where, >> on a first look is not immediately clear which way the compiler >> should go (it's kind of hard to mentally keep track of which classes >> have members that can be accessed), or how they should be translated. >> It probably doesn't help that the implementation seems to be all over >> the place in this area. >> >> So, my argument is mostly a pedagogical one, e.g. drawing a clear >> line that is less prone to mis-interpretation and might make the code >> slightly more readable and predictable. >> >> Maurizio > > Here's a different perspective that could also affect the pedagogical > aspect: > > I've seen academic approaches that tackle the initialization issue by > discriminating two variants of every reference type T: > * type "raw T" captures instances that are not yet fully initialized > * type "cooked T" is the type of instances with initialization completed > > (sorry for overloading 'raw' in this context, I only wish the > distinction raw-parameterized could be retired completely :) ). > > So, within a constructor 'this' always has a "raw" type, which implies > that the things you can do with that value are limited. (In a fully > elaborated system this would even allow post-constructor > initialization without risk of exposing uninitialized fields, or > select which methods can be safely invoked on a "raw" this pointer -- > not to mention the great benefit for NPE-analysis). Yes, and this is more or less how the JVM type system goes about this business. > > Perhaps some years from now Java will even adopt such type modifiers, > but already for the discussion at hand I'd think that the existence of > any "Outer.this" can be determined purely based on lexical nesting > (unless 'static' is said explicitly), but semantic analysis will know > that some of these references will have a "raw type", imposing the > necessary restrictions. Maybe existence of enclosing instance > references is not boolean but three-valued: absent-raw-cooked. > > IOW, I see nothing awkward with enclosing instance references that > impose some restrictions on their usage. OTOH the implementation > pattern "this.this$0" does *not* require "this" to be fully cooked, > right? I believe you are incorrect here. If you want to access "this.this$0", then "this" has to be fully cooked, or you're in for a verifier error. This is the source of complexity in the implementation (and the cause of all the bugs we're seeing): the compiler "thinks" there's some reachable enclosing instance via some "this.this$x.this$y..." chain, but that's not the case (as that chain contains "missing links"). A more robust approach would be for the local class to capture each and every enclosing instance that it uses inside its body, and do not rely on a chain of "this.this$x.this$y..." at all. Another approach would be to still have a chain, but have the chain "skip" over the inaccessible enclosing instances. Maurizio > > best, > Stephan From archie.cobbs at gmail.com Tue Jun 11 15:28:34 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Tue, 11 Jun 2024 10:28:34 -0500 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: <022bdbec-dfb0-4b65-96c0-797e316197aa@oracle.com> References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> <3f0c6d7e-b57e-4f49-aa0a-4ee7c58dba38@oracle.com> <022bdbec-dfb0-4b65-96c0-797e316197aa@oracle.com> Message-ID: On Tue, Jun 11, 2024 at 3:53?AM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > So, back to Attr - turns out that this code (of which I?m not very proud > of :-) ), can be rewritted w/o accessing chk.basicHandler inside the anon > class. > Sure, you could do that - but that's really beside the point. The point I'm making is simply to show that incidences of classes declared in early construction contexts accessing outer instances exist out there in the world. They may be rare, but they are not so rare that they can be dismissed - at least not according to the high Java/openjdk backward compatibility standards that I've observed over the years. Do you disagree? > Thinking more, I think I can perhaps find an argument in defense of the > strategy of the approach outlined in the current JEP: migrating lambdas to > classes. > > So, back to your example: > > class Outer > int x; > class Inner extends Foo { > Inner() { > super(new Bar() { > { Outer.this.x++; } // allowed??? > }); > } > } > } > > Let?s say we write this using lambdas: > > class Outer > int x; > class Inner extends Foo { > Inner() { > super(() -> { ... Outer.this.x++ ... }); > }); > } > } > } > > This is now ok, as the lambda has access to any enclosing instances that > were available at the time the lambda was created (e.g. Outer.this). > I'm not sure I understand... I don't see how an inner class vs. a lambda are any different with respect to how outer instances are handled or can be accessed. In both cases, the constructor is going to have to construct some object and pass as a synthetic parameter a copy of the synthetic "Outer.this" parameter provided to the Inner() constructor. In neither case can the constructor pass the Inner "this" instance at that time, because it is still uninitialized. To be clear, here is how the JDK 17 compiler compiles the two variants of this class: import java.util.concurrent.atomic.AtomicReference; class Outer { int x; class Inner extends AtomicReference { Inner() { super(new Runnable() { public void run() { Outer.this.x++; } }); // variant #1 super(() -> Outer.this.x++); // variant #2 } } } Variant #1: Outer$Inner(Outer); Code: 0: aload_0 1: aload_1 2: putfield #1 // Field this$0:LOuter; 5: aload_0 6: new #7 // class Outer$Inner$1 9: dup 10: aload_1 11: invokespecial #9 // Method Outer$Inner$1."":(LOuter;)V 14: invokespecial #13 // Method java/util/concurrent/atomic/AtomicReference."":(Ljava/lang/Object;)V 17: return Variant #2: Outer$Inner(Outer); Code: 0: aload_0 1: aload_1 2: putfield #1 // Field this$0:LOuter; 5: aload_0 6: aload_1 7: invokedynamic #7, 0 // InvokeDynamic #0:run:(LOuter;)Ljava/lang/Runnable; 12: invokespecial #11 // Method java/util/concurrent/atomic/AtomicReference."":(Ljava/lang/Object;)V 15: return In both cases, the synthetic Outer.this passed to the Inner() constructor is in turn passed to the Runnable or lambda. In both cases it would be illegal to instead access this$0 directly. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Tue Jun 11 15:35:35 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Tue, 11 Jun 2024 16:35:35 +0100 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> <3f0c6d7e-b57e-4f49-aa0a-4ee7c58dba38@oracle.com> <022bdbec-dfb0-4b65-96c0-797e316197aa@oracle.com> Message-ID: On 11/06/2024 16:28, Archie Cobbs wrote: > On Tue, Jun 11, 2024 at 3:53?AM Maurizio Cimadamore > wrote: > > So, back to Attr - turns out that this code (of which I?m not very > proud of :-) ), can be rewritted w/o accessing chk.basicHandler > inside the anon class. > > > Sure, you could do that - but that's really beside the point. > > The point I'm making is simply to show that incidences of classes > declared in early construction contexts accessing outer instances > exist out there in the world. They may be rare, but they are not so > rare that they can be dismissed - at least not according to the high > Java/openjdk backward compatibility standards that I've observed over > the years. Do you disagree? I think so. At times we have made other source incompatible changes (esp. in the context of type inference in Java 8) with the goal of putting the language on a firmer ground. So far the evidence is that this code idiom is basically non-existent in the wild, and there's workarounds. So that, alone, wouldn't constitute a great argument IMHO. > Thinking more, I think I can perhaps find an argument in defense > of the strategy of the approach outlined in the current JEP: > migrating lambdas to classes. > > So, back to your example: > > |class Outer int x; class Inner extends Foo { Inner() { super(new > Bar() { { Outer.this.x++; } // allowed??? }); } } } | > > Let?s say we write this using lambdas: > > |class Outer int x; class Inner extends Foo { Inner() { super(() > -> { ... Outer.this.x++ ... }); }); } } } | > > This is now ok, as the lambda has access to any enclosing > instances that were available at the time the lambda was created > (e.g. Outer.this). > > I'm not sure I understand... I don't see how an inner class vs. a > lambda are any different with respect to how outer instances are > handled or can be accessed. In both cases, the constructor is going to > have to construct some object and pass as a synthetic parameter a copy > of the synthetic "Outer.this" parameter provided to the Inner() > constructor. In neither case can the constructor pass the Inner "this" > instance at that time, because it is still uninitialized. I don't want to dive in the specifics on how things are implemented, which is beyond the point I was making. My point is that we'd probably want the two examples above to align, which probably means going with the rules in JEP 482 (or some alternate rules which achieve the same effects). Maurizio -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Tue Jun 11 15:57:42 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Tue, 11 Jun 2024 10:57:42 -0500 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> <3f0c6d7e-b57e-4f49-aa0a-4ee7c58dba38@oracle.com> <022bdbec-dfb0-4b65-96c0-797e316197aa@oracle.com> Message-ID: On Tue, Jun 11, 2024 at 10:35?AM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > I think so. At times we have made other source incompatible changes (esp. > in the context of type inference in Java 8) with the goal of putting the > language on a firmer ground. > > So far the evidence is that this code idiom is basically non-existent in > the wild, and there's workarounds. So that, alone, wouldn't constitute a > great argument IMHO. > OK - we can have different predictions on how it might play out. My guess is colored by previous experience. For example, my fix for JDK-8294461 ("wrong effectively final determination by javac") got reverted because of backward incompatibility in compiler behavior, and this change in behavior was arguably even more obscure in terms of "code in the wild" affected and it was certainly more justified from a spec point of view because it wasn't changing the spec, it was fixing the compiler to correctly follow the spec (!) > My point is that we'd probably want the two examples above to align, which > probably means going with the rules in JEP 482 (or some alternate rules > which achieve the same effects). > I agree. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From stephan.herrmann at berlin.de Tue Jun 11 16:17:32 2024 From: stephan.herrmann at berlin.de (Stephan Herrmann) Date: Tue, 11 Jun 2024 18:17:32 +0200 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> Message-ID: Am 11.06.24 um 16:05 schrieb Maurizio Cimadamore: > On 11/06/2024 13:37, Stephan Herrmann wrote: > >> Am 10.06.24 um 19:10 schrieb Maurizio Cimadamore: >>> On 10/06/2024 17:32, Archie Cobbs wrote: >>> >>>> On Mon, Jun 10, 2024 at 4:24?AM Maurizio Cimadamore [...] >>>> ??? Historically, the JLS used to treat this/super calls as a static context >>>> ??? (8.8.7.1). >>>> >>>> >>>> That's what the JLS said, but of course that's not what the compiler ever >>>> implemented (JDK-8301649). >>> >>> Sure. What about ecj? What did that compiler implement? (This is more a >>> question for Stephan). >> >> A quick sample shows that flags insideConstructorCall and insideStaticContext >> differ only in what exact error message ecj gives. So, generally ecj does >> implement that rule. Relevant code sections go back to some time around 2003. >> I see no reason to consider that rule as a failure. > > So, to be clear, the Eclipse compiler rejects stuff like the one we?ve been > discussing - e.g. > > |class Outer int x; class Inner extends Foo { Inner() { super(new Bar() { { > Outer.this.x++; } // allowed??? }); } } } | > > Correct? I'm sorry, I didn't look at the specific complications. This particular one is wrongly accepted also by ecj. IOW there is some confusion indeed regarding how far the "staticness" propagates into nested structures. Stephan From maurizio.cimadamore at oracle.com Tue Jun 11 16:36:49 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Tue, 11 Jun 2024 17:36:49 +0100 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> <3f0c6d7e-b57e-4f49-aa0a-4ee7c58dba38@oracle.com> <022bdbec-dfb0-4b65-96c0-797e316197aa@oracle.com> Message-ID: <854fa2c8-feb4-4161-9758-51e74d67139a@oracle.com> On 11/06/2024 16:57, Archie Cobbs wrote: > On Tue, Jun 11, 2024 at 10:35?AM Maurizio Cimadamore > wrote: > > I think so. At times we have made other source incompatible > changes (esp. in the context of type inference in Java 8) with the > goal of putting the language on a firmer ground. > > So far the evidence is that this code idiom is basically > non-existent in the wild, and there's workarounds. So that, alone, > wouldn't constitute a great argument IMHO. > > OK - we can have different predictions on how it might play out. > > My guess is colored by previous experience. For example, my fix for > JDK-8294461 ("wrong effectively final determination by javac") got > reverted because of backward incompatibility in compiler behavior, and > this change in behavior was arguably even more obscure in terms of > "code in the wild" affected and it was certainly more justified from a > spec point of view because it wasn't changing the spec, it was fixing > the compiler to correctly follow the spec (!) I think you are comparing apples and oranges. The issue you mention was backed out as part of this: https://bugs.openjdk.org/browse/JDK-8299416 Which has a very long discussion, mentioning the need of supporting JLS text: https://bugs.openjdk.org/browse/JDK-8299861 In other words, as sometimes happens, following JLS to the letter can reveal other cases where perhaps JLS was underspecified, and in those cases it's ok (even desirable) to take a step back to see what needs to be aligned with what. Maurizio -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Tue Jun 11 17:10:42 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Tue, 11 Jun 2024 18:10:42 +0100 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> Message-ID: On 11/06/2024 17:17, Stephan Herrmann wrote: > I'm sorry, I didn't look at the specific complications. This > particular one is wrongly accepted also by ecj. IOW there is some > confusion indeed regarding how far the "staticness" propagates into > nested structures. Ok. I tried this example: |$ cat Foo.java class Test { interface Bar { } static class Foo { Foo(Bar bar) { } } int x; class Inner extends Foo { Inner() { super(new Bar() { { Test.this.x++; } // allowed??? }); } } } | |java -jar org.eclipse.jdt.core.compiler.batch_3.38.0.v20240524-2033.jar Foo.java ---------- 1. ERROR in /w/lt/jdk/dev/Foo.java (at line 11) { Test.this.x++; } // allowed??? ^^^^^^^^^ No enclosing instance of the type Test is accessible in scope ---------- 1 problem (1 error) | This seems to suggest that the ?outside JLS? behavior is not supported by ecj? Maurizio ? -------------- next part -------------- An HTML attachment was scrubbed... URL: From stephan.herrmann at berlin.de Tue Jun 11 17:11:05 2024 From: stephan.herrmann at berlin.de (Stephan Herrmann) Date: Tue, 11 Jun 2024 19:11:05 +0200 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> <3f0c6d7e-b57e-4f49-aa0a-4ee7c58dba38@oracle.com> <022bdbec-dfb0-4b65-96c0-797e316197aa@oracle.com> Message-ID: <0c678b88-a623-49da-bcc4-310f6b400d0e@berlin.de> Am 11.06.24 um 17:57 schrieb Archie Cobbs: > On Tue, Jun 11, 2024 at 10:35?AM Maurizio Cimadamore > > wrote: > My point is that we'd probably want the two examples above to align, which > probably means going with the rules in JEP 482 (or some alternate rules > which achieve the same effects). > > I agree. > > -Archie Can you help me fully appreciate your agreement? Somewhere in the discussion I lost track which example was presented to prove which point and what the consequences would be. What I took home so far is: * javac and ecj both accept some programs that violate JLS version 22 * JEP 482 has a more consistent set of rules than before * the concept "chain of enclosing instances" can be an illusion (but replacing this.this$0 with val$this$0 could pretend it's real) What I haven't understood: * did you say implementing JEP 482 will cause code to be rejected that is currently accepted? - which examples fall in this category? * some post spoke of tweaking JLS to model what the implemented behavior is. - does anyone still think this is what's happening? - if so, which rule reflects implementation rather than principle? thanks, Stephan From maurizio.cimadamore at oracle.com Tue Jun 11 17:15:32 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Tue, 11 Jun 2024 18:15:32 +0100 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> Message-ID: Ok, I hve replied my own question. The change in behavior in ecj seems to be triggered by -source 22. If no -source option is passed, the program will NOT compile. Maurizio On 11/06/2024 18:10, Maurizio Cimadamore wrote: > > On 11/06/2024 17:17, Stephan Herrmann wrote: > >> I'm sorry, I didn't look at the specific complications. This >> particular one is wrongly accepted also by ecj. IOW there is some >> confusion indeed regarding how far the "staticness" propagates into >> nested structures. > > Ok. I tried this example: > > |$ cat Foo.java class Test { interface Bar { } static class Foo { > Foo(Bar bar) { } } int x; class Inner extends Foo { Inner() { > super(new Bar() { { Test.this.x++; } // allowed??? }); } } } | > |java -jar > org.eclipse.jdt.core.compiler.batch_3.38.0.v20240524-2033.jar Foo.java > ---------- 1. ERROR in /w/lt/jdk/dev/Foo.java (at line 11) { > Test.this.x++; } // allowed??? ^^^^^^^^^ No enclosing instance of the > type Test is accessible in scope ---------- 1 problem (1 error) | > > This seems to suggest that the ?outside JLS? behavior is not supported > by ecj? > > Maurizio > > ? -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Tue Jun 11 17:25:41 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Tue, 11 Jun 2024 18:25:41 +0100 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: <0c678b88-a623-49da-bcc4-310f6b400d0e@berlin.de> References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> <3f0c6d7e-b57e-4f49-aa0a-4ee7c58dba38@oracle.com> <022bdbec-dfb0-4b65-96c0-797e316197aa@oracle.com> <0c678b88-a623-49da-bcc4-310f6b400d0e@berlin.de> Message-ID: <7542448d-bad8-47fc-842b-219b5946541b@oracle.com> On 11/06/2024 18:11, Stephan Herrmann wrote: > Am 11.06.24 um 17:57 schrieb Archie Cobbs: >> On Tue, Jun 11, 2024 at 10:35?AM Maurizio Cimadamore >> > > wrote: >> ??? My point is that we'd probably want the two examples above to >> align, which >> ??? probably means going with the rules in JEP 482 (or some alternate >> rules >> ??? which achieve the same effects). >> >> I agree. >> >> -Archie > > Can you help me fully appreciate your agreement? Somewhere in the > discussion I lost track which example was presented to prove which > point and what the consequences would be. > > What I took home so far is: > * javac and ecj both accept some programs that violate JLS version 22 Yes. And, interestingly, before -source 22, ecj did not accept any of these problematic programs. I think that, in itself, this speaks quite a bit re. how common code like this really is. > * JEP 482 has a more consistent set of rules than before check > * the concept "chain of enclosing instances" can be an illusion > ? (but replacing this.this$0 with val$this$0 could pretend it's real) checl > > What I haven't understood: > * did you say implementing JEP 482 will cause code to be rejected that > is currently accepted? > ? - which examples fall in this category? No, I didn't say that. I believe the changes in JEP 482 correctly specify what the behavior should be, _under the assumption_ that we believe that capturing as many enclosing instances as possible in pre-construction context is a good thing (which it's really why I'm bringing this up). > * some post spoke of tweaking JLS to model what the implemented > behavior is. > ?? - does anyone still think this is what's happening? > ?? - if so, which rule reflects implementation rather than principle? Well, you could see the spec changes in JEP 482 as trying to provide some basis for what javac has been done since 1.4. This doesn't mean the new rules are reflecting "implementation". But the rules do seem biased towards "accepting the biggest possible set of classes in pre-construction context". Now, I don't think the arguments of "we have to support this because code out there use this" is valid. The fact that ecj never supported this is just another piece of evidence that this line of thinking is flawed. BUT, there is a more valid argument, which I brought up in a recent email, where I pointed out that trying to compile as many classes in pre-construction context has a nice effect when it comes to alignment with lambda expressions. A lambda expression does not introduce a _new_ class declaration, so it side-steps all the question about what its enclosing instance is. It can just use whatever enclosing instance is available in the context where the lambda appears. But, if you turn the lambda into a local class, and say (as I proposed) that the local class has no enclosing instance by virtue of being in a pre-construction context, that creates an asymmetry: the lambda expression can access enclosing instances that are not available in its local class counterpart. Whether that's a big enough argument towards supporting JEP 482 in all its ramification I don't now (and I'd have to think more about it). But at least this is an argument that I can see us defend (e.g. code refactoring is an important use case). Maurizio > > thanks, > Stephan From stephan.herrmann at berlin.de Tue Jun 11 17:43:22 2024 From: stephan.herrmann at berlin.de (Stephan Herrmann) Date: Tue, 11 Jun 2024 19:43:22 +0200 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <89c6e982-5129-47bf-950d-27b5ebe51eb6@berlin.de> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> Message-ID: <5fc04558-c4f1-4148-9201-3fec83a64051@berlin.de> Am 11.06.24 um 19:10 schrieb Maurizio Cimadamore: > On 11/06/2024 17:17, Stephan Herrmann wrote: > >> I'm sorry, I didn't look at the specific complications. This particular one is >> wrongly accepted also by ecj. IOW there is some confusion indeed regarding how >> far the "staticness" propagates into nested structures. > > Ok. I tried this example: > > |$ cat Foo.java class Test { interface Bar { } static class Foo { Foo(Bar bar) { > } } int x; class Inner extends Foo { Inner() { super(new Bar() { { > Test.this.x++; } // allowed??? }); } } } | > > |java -jar org.eclipse.jdt.core.compiler.batch_3.38.0.v20240524-2033.jar > Foo.java ---------- 1. ERROR in /w/lt/jdk/dev/Foo.java (at line 11) { > Test.this.x++; } // allowed??? ^^^^^^^^^ No enclosing instance of the type Test > is accessible in scope ---------- 1 problem (1 error) | > > This seems to suggest that the ?outside JLS? behavior is not supported by ecj? With this invocation you implicitly compiled for compliance 1.4 (which will probably be dropped soon, at last). At 1.5+ ecj accepts. Frankly, that change was before my time, but perhaps this code comment serves for entertainment: // From 1.5 on, provide access to enclosing instance synthetic constructor argument when declared inside constructor call // only for direct anonymous type //public class X { // void foo() {} // class M { // M(Object o) {} // M() { this(new Object() { void baz() { foo(); }}); } // access to #foo() indirects through constructor synthetic arg: val$this$0 // } //} Stephan From stephan.herrmann at berlin.de Tue Jun 11 17:49:56 2024 From: stephan.herrmann at berlin.de (Stephan Herrmann) Date: Tue, 11 Jun 2024 19:49:56 +0200 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: <7542448d-bad8-47fc-842b-219b5946541b@oracle.com> References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> <3f0c6d7e-b57e-4f49-aa0a-4ee7c58dba38@oracle.com> <022bdbec-dfb0-4b65-96c0-797e316197aa@oracle.com> <0c678b88-a623-49da-bcc4-310f6b400d0e@berlin.de> <7542448d-bad8-47fc-842b-219b5946541b@oracle.com> Message-ID: Am 11.06.24 um 19:25 schrieb Maurizio Cimadamore: >> What?I?took?home?so?far?is: >> *?javac?and?ecj?both?accept?some?programs?that?violate?JLS?version?22 > Yes. And, interestingly, before -source 22, ecj did not accept any of these > problematic programs. Correction as per my other post - you are typing faster than I can research :) => before -source 1.5, ecj did not accept any of these problematic programs. > I think that, in itself, this speaks quite a bit?re.?how?common?code?like?this?really?is. not a strong point really Stephan From archie.cobbs at gmail.com Tue Jun 11 18:12:50 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Tue, 11 Jun 2024 13:12:50 -0500 Subject: Draft Spec for Second Preview of Flexible Constructor Bodies (JEP 482) In-Reply-To: <0c678b88-a623-49da-bcc4-310f6b400d0e@berlin.de> References: <125ABABF-4D7C-4B57-B5D2-E81C3FC86FB1@oracle.com> <33ce0900-5b19-4d73-8f66-e94477d28fc9@oracle.com> <5619d1fd-b14a-432e-9355-e1d4caaa423e@berlin.de> <9259a062-f0a8-420e-8061-736ac5567e8f@oracle.com> <9a48af8d-910c-45a2-972e-d474807f9172@oracle.com> <3f0c6d7e-b57e-4f49-aa0a-4ee7c58dba38@oracle.com> <022bdbec-dfb0-4b65-96c0-797e316197aa@oracle.com> <0c678b88-a623-49da-bcc4-310f6b400d0e@berlin.de> Message-ID: On Tue, Jun 11, 2024 at 12:11?PM Stephan Herrmann < stephan.herrmann at berlin.de> wrote: > Am 11.06.24 um 17:57 schrieb Archie Cobbs: > > On Tue, Jun 11, 2024 at 10:35?AM Maurizio Cimadamore > > > > wrote: > > My point is that we'd probably want the two examples above to align, > which > > probably means going with the rules in JEP 482 (or some alternate > rules > > which achieve the same effects). > > > > I agree. > > Can you help me fully appreciate your agreement? Somewhere in the > discussion I > lost track which example was presented to prove which point and what the > consequences would be. > I'm simply agreeing that (a) we should stick with the rules in JEP 482 and (b) how an anonymous class and the lambda equivalent are treated should be consistent (which is what you get with (a)). > What I took home so far is: > * javac and ecj both accept some programs that violate JLS version 22 > Yes - and note, it's not just about outer instances. The JLS says "static context", which means no generic type variables, etc. See JDK-8301649 . > * the concept "chain of enclosing instances" can be an illusion > (but replacing this.this$0 with val$this$0 could pretend it's real) > There are really two illusions, which the compiler has to deal with internally because happen "below" the specification, and these cause a bunch of compiler pain/complexity: - An indirect outer instance is not accessed directly but must be accessed by "hopping" through a direct outer instance - The synthetic instance field that stores an outer instance cannot be used in an early construction context, so the constructor bytecode has to use the synthetic constructor parameter that provided it directly instead. * did you say implementing JEP 482 will cause code to be rejected that is > currently accepted? > No! JEP 482 should be a strict expansion of the set of valid source files. This is true both at a specification level and a "what javac compiles" level (these levels are different because the compiler doesn't perfectly match the specification). > * some post spoke of tweaking JLS to model what the implemented behavior > is. > - does anyone still think this is what's happening? > - if so, which rule reflects implementation rather than principle? > As far as I know, *modulo bugs*, the implemented behavior in javac matches JEP 482. The current *modulo bugs* are for example JDK-8333766 , JDK-8333822 , JDK-8334037 . I'm trying to help figure these out but so far am flummoxed. Maurizio has made some progress though and I'm hoping he'll keep going :) -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Fri Jun 14 20:52:29 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Fri, 14 Jun 2024 15:52:29 -0500 Subject: Local classes and static members? Message-ID: Dumb question... Where is it specified in the JLS that a (normal) local class cannot have static members? I don't see it in ?14.3 . Thanks. -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Fri Jun 14 22:50:13 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Fri, 14 Jun 2024 17:50:13 -0500 Subject: Local classes and static members? In-Reply-To: References: Message-ID: Answering my own question... It's a dumb question because it's no longer true. Originally local classes could not have static members but that changed in JDK 16 with JDK-8254321 . -Archie On Fri, Jun 14, 2024 at 3:52?PM Archie Cobbs wrote: > Dumb question... > > Where is it specified in the JLS that a (normal) local class cannot have > static members? I don't see it in ?14.3 > . > > Thanks. > > -- > Archie L. Cobbs > -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sat Jun 15 06:01:21 2024 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 15 Jun 2024 08:01:21 +0200 (CEST) Subject: Local classes and static members? In-Reply-To: References: Message-ID: <637327551.50061674.1718431281024.JavaMail.zimbra@univ-eiffel.fr> > From: "Archie Cobbs" > To: "amber-spec-experts" > Sent: Saturday, June 15, 2024 12:50:13 AM > Subject: Re: Local classes and static members? > Answering my own question... > It's a dumb question because it's no longer true. Originally local classes could > not have static members but that changed in JDK 16 with [ > https://bugs.openjdk.org/browse/JDK-8254321 | JDK-8254321 ] . yes, at the time records were introduced. The only missing related feature is to allow the keyword 'static' in front of a local class, which is useful when you want to sandbox a static local class into a method by example in unit tests. > -Archie R?mi > On Fri, Jun 14, 2024 at 3:52 PM Archie Cobbs < [ mailto:archie.cobbs at gmail.com | > archie.cobbs at gmail.com ] > wrote: >> Dumb question... >> Where is it specified in the JLS that a (normal) local class cannot have static >> members? I don't see it in [ >> https://docs.oracle.com/javase/specs/jls/se22/html/jls-14.html#jls-14.3 | ?14.3 >> ] . >> Thanks. >> -- >> Archie L. Cobbs > -- > Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: