Re: Superclasses for inline classes

2020-02-14 Thread forax
- Mail original -
> De: "daniel smith" 
> À: "Remi Forax" 
> Cc: "valhalla-spec-experts" 
> Envoyé: Jeudi 13 Février 2020 15:04:41
> Objet: Re: Superclasses for inline classes

>> On Feb 13, 2020, at 3:57 AM, fo...@univ-mlv.fr wrote:
>> 
>> - Mail original -
>>> De: "daniel smith" 
>>> À: "Remi Forax" 
>>> Cc: "valhalla-spec-experts" 
>>> Envoyé: Jeudi 13 Février 2020 00:49:34
>>> Objet: Re: Superclasses for inline classes
>> 
>>>> On Feb 12, 2020, at 11:41 AM, Remi Forax  wrote:
>>>> 
>>>> a garbage class like java.util.Collections (with an 's' at the end) 
>>>> validate all
>>>> the conditions but should not have an abstract constructor.
>>> 
>>> Why not? If identity classes can extend it, and it has no 
>>> state/initialization,
>>> why not inline classes too?
>> 
>> Sorry to not be clear, because it's not a backward compatible change,
>> the empty constructor becomes abstract.
> 
> The JVM and language features (all 3 variations) are designed to ensure that 
> the
> default constructor -> abstract constructor change is fully compatible. Can 
> you
> illustrate what you think would not be compatible?

What am saying is that garbage classes either are declared abstract or have a 
private constructor to avoid to be instantiated,
the former will be eligible for (4) and not the later, so some garbage classes 
will become treated differently by the VM, will have their javadoc altered, the 
API seen by reflection changed, etc, even if it was not the intent of their 
authors.

You're right that an abstract garbage class can be sub-classed, yes, using a 
private constructor that throws a NoSuchMethodError is a better design, but 
that not the point, the point is why retrofitting an existing class while it's 
not clear that it was the author intent. 

For me (4) is too clever with little benefit. We want to introduce a new kind 
of abstract class with a slightly different semantics that the one existing. 
Playing the retrofit game here doesn't worth the trouble.

Rémi


Re: Superclasses for inline classes

2020-02-13 Thread Dan Smith


> On Feb 13, 2020, at 3:57 AM, fo...@univ-mlv.fr wrote:
> 
> - Mail original -
>> De: "daniel smith" 
>> À: "Remi Forax" 
>> Cc: "valhalla-spec-experts" 
>> Envoyé: Jeudi 13 Février 2020 00:49:34
>> Objet: Re: Superclasses for inline classes
> 
>>> On Feb 12, 2020, at 11:41 AM, Remi Forax  wrote:
>>> 
>>> a garbage class like java.util.Collections (with an 's' at the end) 
>>> validate all
>>> the conditions but should not have an abstract constructor.
>> 
>> Why not? If identity classes can extend it, and it has no 
>> state/initialization,
>> why not inline classes too?
> 
> Sorry to not be clear, because it's not a backward compatible change,
> the empty constructor becomes abstract.

The JVM and language features (all 3 variations) are designed to ensure that 
the default constructor -> abstract constructor change is fully compatible. Can 
you illustrate what you think would not be compatible?

> There is a lot of classes like that in the wild, and given that the JLS 
> allows to call static methods on an instance, there are existing code that 
> are using the default constructor even if the author of the class never 
> wanted to allow such usage.

If there is a class that allows unwanted subclasses, it will continue to allow 
unwanted subclasses. The way to change that is to make it 'final' or declare a 
'private' constructor.

Re: Superclasses for inline classes

2020-02-13 Thread forax
- Mail original -
> De: "daniel smith" 
> À: "Remi Forax" 
> Cc: "valhalla-spec-experts" 
> Envoyé: Jeudi 13 Février 2020 00:49:34
> Objet: Re: Superclasses for inline classes

>> On Feb 12, 2020, at 11:41 AM, Remi Forax  wrote:
>> 
>> a garbage class like java.util.Collections (with an 's' at the end) validate 
>> all
>> the conditions but should not have an abstract constructor.
> 
> Why not? If identity classes can extend it, and it has no 
> state/initialization,
> why not inline classes too?

Sorry to not be clear, because it's not a backward compatible change,
the empty constructor becomes abstract.

There is a lot of classes like that in the wild, and given that the JLS allows 
to call static methods on an instance, there are existing code that are using 
the default constructor even if the author of the class never wanted to allow 
such usage.

Rémi



Re: Superclasses for inline classes

2020-02-12 Thread Dan Smith
> On Feb 12, 2020, at 11:41 AM, Remi Forax  wrote:
> 
> a garbage class like java.util.Collections (with an 's' at the end) validate 
> all the conditions but should not have an abstract constructor.

Why not? If identity classes can extend it, and it has no state/initialization, 
why not inline classes too?

(In reality, this particular class has chosen to actively prevent subclassing 
and instantiation by declaring a private constructor; it wouldn't qualify):

public class Collections {
// Suppresses default constructor, ensuring non-instantiability.
private Collections() {
}



Re: Superclasses for inline classes

2020-02-12 Thread Dan Smith
> On Feb 11, 2020, at 4:54 PM, Dan Smith  wrote:
> 
> Availability: Ideally, extending a suitable abstract class should be 
> frictionless for inline class authors. In (2) and (3), the authors are 
> blocked until the abstract class author opts in. In (4), the opt in is by 
> default, although there's still a requirement that the abstract class be 
> compiled with an appropriate source version.
> 
> In practice, if we require an opt in, this will be an obscure, little-used 
> feature; or maybe it will become widespread, but we'll have introduced some 
> new standard boilerplate into the language. If we don't require an opt in, 
> inline classes will be free to extend most abstract classes automatically.

Dan H raised a good point about availability on the call today: some tools like 
to instrument constructors, including empty/default ones. If we recompile most 
of the abstract classes in the world to make them instrumentation-hostile (as 
is the case if they have ACC_ABSTRACT  methods), some of those tools are 
going to break, perhaps with no good workaround for the behavior they want.

So there's a trade-off: widespread inline-friendly abstract classes will make 
inline class authors happy, but instrumentation tools sad.

I don't have a sense of how serious this problem is, but it's something we 
should get a better handle on.



Re: Superclasses for inline classes

2020-02-12 Thread Remi Forax
Hi all,
sorry, was not available for the meeting today (i'm officially on vacation).

I prefer (2) with a restriction like (1).

I don't think users should be able to declare this kind of abstract class 
because you can not evolve them easily. I'm worried that if they become a tool 
available, people will over-use them (they are like a kind of abstract class 
with super-power, as a user it's like a shiny cookie), so in the end we will 
have to introduce inheritance of inline classes.
This rule out (3).

What (4) try to do is too clever IMO, a garbage class like 
java.util.Collections (with an 's' at the end) validate all the conditions but 
should not have an abstract constructor.
It's too easy to create such kind of abstract class without knowing it.

So for me, it's (2) + (1), at language level, a constructor can be declared 
abstract but it's restricted to class inside java.lang or java.base (enforced 
by the compiler and the VM).

Rémi

- Mail original -
> De: "daniel smith" 
> À: "valhalla-spec-experts" 
> Envoyé: Mercredi 12 Février 2020 00:54:10
> Objet: Re: Superclasses for inline classes

> So I think the JVM story is fairly settled; I've spun it off into another
> thread, with a proposed spec.
> 
> The language story is still uncertain. Here are four alternative designs, all 
> of
> which are, I think, reasonable approaches to consider. Some discussion about
> trade-offs is at the end.
> 
> -
> 
> Alternative 1: No language support
> 
> The language requires all inline classes to extend Object (or perhaps 
> prohibits
> the 'extends' clause). Abstract  methods are available only as a 
> compiler
> tool.
> 
> Some special exceptions are necessary:
> 
> - Somehow, class files for Object and Number must be generated with abstract
>  methods
> 
> - Integer, Double, etc., are inline classes but are allowed to extend Number
> 
> -
> 
> Alternative 2: Abstract constructors in the language
> 
> abstract class C {
>public abstract C();
> }
> 
> Like a method, a constructor can be declared 'abstract' and omit a body.
> (Bikeshed: maybe the 'abstract' keyword is spelled differently, or left off
> entirely.)
> 
> These declarations can align closely with the JVM's rules for  methods:
> - The superclass must also have an abstract constructor
> - No instance initializers or instance field initializers are allowed
> - It's okay to overload the constructor, but 'abstract' only works on a no-arg
> constructor
> - (Perhaps) the class doesn't have to be abstract
> - It's allowed (as a no-op) to invoke the constructor (new Object() or 
> super())
> 
> We'll need one further restriction that isn't checked by the JVM and isn't as
> principled: no instance fields are allowed, even if they're
> default-initialized. Otherwise, inline classes will have to search for private
> fields to decide if extension is legal or not, breaking encapsulation.
> 
> The abstract-ness of the constructor is part of the API—appears in javadoc,
> changing it is incompatible. Having an empty or default constructor isn't the
> same as having an abstract constructor.
> 
> Inline classes can only extend classes with abstract constructors (and maybe 
> no
> 'synchronized' instance methods).
> 
> -
> 
> Alternative 3: Inline-friendly property in the language
> 
> inlineable abstract class C {
> }
> 
> The 'inlineable' property (bikeshed: how to spell this?) enforces some
> constraints and authorizes children to be inline classes.
> 
> Specific constraints:
> - Can't have instance fields
> - Can't declare a constructor or instance initializer
> - Must extend an inlineable class
> - Must be an abstract class or Object (maybe?)
> - Must not have synchronized methods (probably?)
> 
> An annotation spelling is an option, too, although that crosses a 
> line—there's a
> precedent for annotations that prompt compiler errors, but not for annotations
> that influence bytecode output.
> 
> An inlineable class's bytecode has an abstract  method.
> 
> The 'inlineable' property is part of the API—appears in javadoc, changing it 
> is
> incompatible.
> 
> Inline classes can only extend inlineable classes.
> 
> -
> 
> Alternative 4: Infer no constructor
> 
> Typically, for each class that lacks a constructor declaration, a default
> constructor is provided that does some simple initialization. But in the
> following circumstances, we claim there is no constructor at all:
> 
> - Class doesn't declare a constructor or insta

Re: Superclasses for inline classes

2020-02-12 Thread Brian Goetz
> The language story is still uncertain. Here are four alternative designs, all 
> of which are, I think, reasonable approaches to consider. Some discussion 
> about trade-offs is at the end.

Thanks for pulling this together into a nice menu, with chef’s recommendations. 
 Let me add a few notes, which are mostly “soft” concerns.  

Overall, having fewer “feature X can’t work with Y” restrictions is better; 
alternative 1 (no language support) creates a “inline classes can’t extend 
abstract classes” interaction.  WE know that there are at least a few abstract 
classes we want to have play nicely with inline classes: Number and Record 
immediately come to mind, but AbstractCollection is also a strong candidate.  
(AbstractList has fields, and therefore is a useful example because these 
fields are private and serve the implementation, which illustrates the dangers 
Dan is concerned about: that AbstractList starts out being like 
AbstractCollection, but then finds itself constrained in its evolution because 
it has inline subclasses.)  

I agree that giving this feature too-prominent billing (such as 
`inline-friendly abstract class`) is a bad trade.  

I also have no problem with the language inferring whether the superclass is 
suitable for extension by inline classes; this isn’t much different than 
requiring an accessible constructor.  Some classes are suitable  for extension 
by some other classes under some circumstances; as long as the rules are clear 
enough, and the compiler error messages are helpful when you cross the line, I 
don’t need a special opt in to say “It’s OK for this class to be extended by 
inline classes.”  

The problematic cases are all when abstract class C is extended by inline class 
V, _and C and V are in different maintenance domains_.  If the two are in the 
same maintenance domain (package or module), then refactoring one may have 
consequences on the other, but the two can be co-refactored to avoid the 
problem.  (This is just like refactoring an enum to be a class; there are 
pitfalls, but if the two are in the same maintenance domain, they can be more 
easily navigated.)  So, the problematic case is:

 - A is a public abstract class in maintenance domain M, which is currently 
suitable for extension by inline classes
 - V is an inline class that extends A, in a separate maintenance domain N
 - Later, A wishes to refactor to have, say, private fields, but this would 
break V.

This is surely a possible nasty surprise; the question is how often this is 
going to happen, and how much it would cost to prevent it from happening.  
Based on the numbers we got from Google’s code base (thanks!), it looks like 
~3% of classes are public abstract classes, and many of those would meet the 
requirements to be extended by inline classes.  But, less-than-3% is still a 
small number, and cross-maintenance-domain extension is rarer.  (This is a 
mostly closed codebase; if this were a library, I suspect suitable exposed 
abstract classes would be even rarer, as all the style guidance suggests 
preferring exposing interfaces to abstract classes where possible.)  So I think 
the chain of events that are required to cause regret (publicly exposed 
abstract class, cross maintenance-domain extension, and subsequent incompatible 
evolution) likely constitute a corner-of-a-corner case.  This argues for 
something that minimizes the intrusion into the language.  

Why do we encourage people to expose interfaces and not abstract classes in 
APIs?  Because interfaces are “more abstract”, and therefore exposing them is 
more constraining.  So another lever at our disposal is: dial up the 
style-guide suggestions about not exposing abstract classes across maintenance 
domains.  

Finally, there’s some mitigation if the bad case comes to pass.  Suppose we 
have:

 public abstract class Foo { /* accidentally inline friendly */ }

and later you want to add fields to Foo and do:

public abstract class Foo { 
int field;
Foo(int f) { … }
}

private class Bar extends Foo { … }

but can’t do so because some external inline class has extended it.  You still 
have an option:

public abstract class Foo { /* stays inline friendly */ }

private abstract class SonOfFoo extends Foo { 
// all the non-inline-friendliness you want 
}

private class Bar extends SonOfFoo { … }

So, even though the author made a “mistake” by exposing the abstract class and 
therefore constraining themselves forever, the workaround isn’t so bad.  

Of the options, I’m still liking (4), backed up by an annotation like 
@FunctionalInterface that turns on extra type checking and documentation but 
serves only to capture design intent.  


> 
> -
> 
> Alternative 1: No language support
> 
> The language requires all inline classes to extend Object (or perhaps 
> prohibits the 'extends' clause). Abstract  methods are available only 
> as a compiler tool.
> 
> Some special exceptions

Re: Superclasses for inline classes

2020-02-11 Thread Dan Smith
So I think the JVM story is fairly settled; I've spun it off into another 
thread, with a proposed spec.

The language story is still uncertain. Here are four alternative designs, all 
of which are, I think, reasonable approaches to consider. Some discussion about 
trade-offs is at the end.

-

Alternative 1: No language support

The language requires all inline classes to extend Object (or perhaps prohibits 
the 'extends' clause). Abstract  methods are available only as a compiler 
tool.

Some special exceptions are necessary:

- Somehow, class files for Object and Number must be generated with abstract 
 methods

- Integer, Double, etc., are inline classes but are allowed to extend Number

-

Alternative 2: Abstract constructors in the language

abstract class C {
public abstract C();
}

Like a method, a constructor can be declared 'abstract' and omit a body. 
(Bikeshed: maybe the 'abstract' keyword is spelled differently, or left off 
entirely.)

These declarations can align closely with the JVM's rules for  methods:
- The superclass must also have an abstract constructor
- No instance initializers or instance field initializers are allowed
- It's okay to overload the constructor, but 'abstract' only works on a no-arg 
constructor
- (Perhaps) the class doesn't have to be abstract
- It's allowed (as a no-op) to invoke the constructor (new Object() or super())

We'll need one further restriction that isn't checked by the JVM and isn't as 
principled: no instance fields are allowed, even if they're 
default-initialized. Otherwise, inline classes will have to search for private 
fields to decide if extension is legal or not, breaking encapsulation.

The abstract-ness of the constructor is part of the API—appears in javadoc, 
changing it is incompatible. Having an empty or default constructor isn't the 
same as having an abstract constructor.

Inline classes can only extend classes with abstract constructors (and maybe no 
'synchronized' instance methods).

-

Alternative 3: Inline-friendly property in the language

inlineable abstract class C {
}

The 'inlineable' property (bikeshed: how to spell this?) enforces some 
constraints and authorizes children to be inline classes.

Specific constraints:
- Can't have instance fields
- Can't declare a constructor or instance initializer
- Must extend an inlineable class
- Must be an abstract class or Object (maybe?)
- Must not have synchronized methods (probably?)

An annotation spelling is an option, too, although that crosses a line—there's 
a precedent for annotations that prompt compiler errors, but not for 
annotations that influence bytecode output.

An inlineable class's bytecode has an abstract  method.

The 'inlineable' property is part of the API—appears in javadoc, changing it is 
incompatible.

Inline classes can only extend inlineable classes.

-

Alternative 4: Infer no constructor

Typically, for each class that lacks a constructor declaration, a default 
constructor is provided that does some simple initialization. But in the 
following circumstances, we claim there is no constructor at all:

- Class doesn't declare a constructor or instance initializer
- Class doesn't declare fields
- Superclass has no constructor
- Class is abstract or Object (maybe?)

Again, blank private fields may not *need* initialization, but inline 
subclasses need to know that they exist, and the best way to communicate that 
is through the constructor.

'new Object()' (and perhaps 'new Foo()' if we drop the abstract class 
requirement) doesn't reference any constructor at all. In that case, a fresh 
instance is allocated without any code execution.

For compatibility, this also applies to 'super()' (although we can discourage 
its use in these cases going forward).

A class without a constructor has, in bytecode, an abstract  method.

The lack of a constructor is part of the API—appears in javadoc, changing it is 
incompatible. An annotation like @FunctionalInterface could help by checking 
that nothing has changed on recompilation.

Inline classes can only extend classes that lack constructors (and maybe 
'synchronized' instance methods).

-

Discussion

Noise level: We don't want to make a big deal about this feature. People 
shouldn't think too much about it. (4) wins in this regard—no new syntax, just 
some hand-waving in the language model about what it actually means when you 
leave out your constructor. (2) introduces a subtle variation on constructor 
declarations, which can generally be overlooked. (3) is a neon invitation to 
treat these classes like a fundamentally new kind of entity.

Compatibility: Adding or removing 'abstract' from an '' method is a 
binary incompatible change, so it's good if that doesn't happen accidentally. 
But it's hard to increase awareness of that commitment while minimizing noise 
level, so there are trade-offs. (2) and (3) both force authors to change 
something. (4) doesn't have that guardrail, and it's quite

Re: Superclasses for inline classes

2020-02-06 Thread Brian Goetz
I've been thinking about this, and gone around in circles a few times.  
The properties laid out here are understandable and easily checkable at 
both compile and run time.  (I might add "no synchronization", or at 
least "no synchronized methods".)


I agree that having some sort of class-level modifier gives this concept 
too much importance.  And normally, I would take the position that a 
property like this would have to appear in the declaration somewhere, to 
capture the fact that conformance to these properties is not merely 
accidental and subject to change tomorrow.  But, lately I've been 
questioning this.


Reasons to have some sort of indicator of "I am an initialization-free 
class":


 - Allows author to capture design intent
 - Allows compiler to type-check conformance to requirements, based on 
design intent
 - Propagates design intent into documentation, where subclasses can 
see that this is safe to extend


But it occurs to me that all of these are within the bounds of what 
_annotations_ are allowed to do; we did the same with 
`@FunctionalInterface` that, while not required, turns on additional 
compile-time type checking and documentation.


I am currently thinking that we can adopt Dan's rules at the language 
level, and have an annotation @InitializationFree, which: (a) declares 
intent to conform to the contract of initialization-freedom, (b) 
unleashes compile-time type checking for same, and (c) causes a blurb to 
be emitted into the Javadoc along the lines of "This is an 
initialization-free class" -- but that the language specification works 
entirely in terms of structural properties.  We can say that "inline 
classes may extend initialization-free inline classes", and be done.



Regardless of compile-time type checking, we will likely want to do the 
analysis and write the result into the classfile (e.g., an 
InitializationFree class attribute), which can be seen as a 
necessary-but-not-sufficient signal for the VM to allow the class to be 
used as a super for an inline class.  When we go to load a class with 
this attribute, we can verify that it actually conforms:


 - Fields: no fields
 - Constructors/initializers: no ctor/initializer
 - Class declaration: it is abstract, and its super is i-free
 - Synchronization: No methods have ACC_SYCHRONIZED

When we go to load an inline class, we check the i-free bit on the 
superclass.


None of these checks seem all that burdensome to the VM.

It does mean that dusty abstract classes need to be recompiled before 
they can be bases for inline classes, which, on balance, seems OK.



Language model

An inline class may extend another class, as long as the superclass has the 
following properties:
- It has no instance fields
- It has no constructors
- It has no instance initializers
- It is abstract* or Object
- It extends another class with these properties

Subtype polymorphism works the same for superclasses as it does for 
superinterfaces.

(*Remi points out that we could drop the 'abstract' restriction, 
meaning there may be identity instances of the superclass. Given the 
restriction on fields, though, I'm struggling to envision a use case; 
the consensus is that 'new Object()' is probably something we want to 
*stop*  supporting.)


Call a class that satisfies these constraints an "initialization-free class" 
(bikeshedding on this term is welcome!). Like an interface, its value set may include 
references to both inline class instances and identity class instances.

We*do *not*  want the initialization-free property to be expressed as a class 
modifier—this feature is too obscure to deserve that much prominence, 
encouraging every class author to consider one more degree of freedom; and we 
don't want every class to have to manually opt in.

But we*do*  need the initialization-free property to be part of the public information 
about the class. For example, the javadoc should say something like "this is an 
initialization-free class". Otherwise, it's impossible to tell the difference 
between, e.g., a class with no fields and a class with private fields.

In the past, a Java class declaration that lacks a constructor always got a 
default constructor. In this model, however, an initialization-free class has 
no constructor at all. A 'super()' call directed at such a class is a no-op.




Re: Superclasses for inline classes

2019-12-20 Thread Brian Goetz




I kind of like your idea of a new flavor of constructor. If we drop the inference piece, 
it forces us to surface something in the language, but maybe it's subtle enough that, 
unlike a new flavor of class, it doesn't trip the "must include in Java 101" 
wire?


While I have little faith in textbook authors to correctly make this 
call (based on having reviewed dozens of textbooks), yes, this seems a 
reasonable balance of the concerns.


Re: Superclasses for inline classes

2019-12-20 Thread Dan Smith
A few of points in this thread emphasize differences between *declared* 
inline-friendly abstract classes and *implicitly* inline-friendly abstract 
classes.

> On Dec 20, 2019, at 2:11 PM, John Rose  wrote:
> 
>> - it has no synchronized methods
> 
> I think the natural way to phrase this is that the type itself does not
> admit synchronization, either via a synchronized method or a
> synchronized statement.  Then it’s a type system property rather
> than a structural property of methods.

If the class is declared inline-friendly, then sure, this is a check we can 
make.

If not, we can either:
- Infer from the use of 'synchronized' that this is *not* an inline-friendly 
class
- Call the class inline-friendly anyway, and let it blow up at run time

A compile-time error is not an option.

I lean towards the runtime error, because inferring a class property based on a 
random method modifier is too subtle.

Put another way, if you want strong checking for 'synchronized' (and I think we 
can safely call this an option, not a necessity), you also probably want some 
sort of explicit opt-in to inline friendliness.

>> Object, and all interfaces, would be inline-friendly (we can adjust the 
>> declaration of Object to meet this requirement); the compiler would 
>> structurally recognize abstract classes as inline-friendly and set the bits 
>> in the classfile.
> 
> I like this, as long as “structurally” includes some explicit signal either 
> in the
> source code or (at least) in superclass (from which a new default would be
> silently inferred).  IMO there’s it’s hard to see a case for “promoting”
> apparently-empty constructors (like C(){}) into declaratively-empty ones.
> They would become invisibly-non-empty via action at a distance in supers
> and in field definitions.

Hmm, action at a distance is inevitable in the "just infer it" model. Somebody 
decides to add an innocent-seeming private field, and they've broken binary 
compatibility with their subclasses.

Are we being too clever? We need the class to give us a permanent guarantee 
about the API; but at the same time, we're hoping the author doesn't have to 
notice that they're making this promise. That may not be workable.

I kind of like your idea of a new flavor of constructor. If we drop the 
inference piece, it forces us to surface something in the language, but maybe 
it's subtle enough that, unlike a new flavor of class, it doesn't trip the 
"must include in Java 101" wire?



Re: Superclasses for inline classes

2019-12-20 Thread John Rose
On Dec 20, 2019, at 7:59 AM, Brian Goetz  wrote:
> 
> Stepping back, the thing that frightened us away from this was the 
> combination of (a) not wanting to have a modifier on abstract classes to 
> indicate inline-friendly,

Perhaps that’s made better by inverting the sense of the “bit” (perhaps 
modifier), so that inline-hostile classes have the burden of marking 
themselves, and inheritance can help with that (but only if the bit is 
inverted).

But inline-friendly types still need to contrive declaratively-empty 
constructors, so it looks like there’s a lot of recompilation in our future.

> and (b) worrying that it was a lot of (brittle) work to structurally detect 
> inline-friendly abstract classes.  Dan has cut this knot by tying it to the 
> presence of a declaratively-empty constructor, which changes the story a lot.

This is a specific case of making abstract classes more like interfaces.  If 
you look carefully at interfaces, you can see that their flexibility derives, 
in large part, from the lack of constructors.  (This in turn implies the lack 
of instance fields, but in a secondary way: You can’t control such fields fully 
without a constructor.)  Having declaratively empty constructors in classes 
(abstract or not!) opens for them some of the same paths that interfaces enjoy.

This reasoning can go the other way, too, to make interfaces more like abstract 
classes.  As I’ve pointed out before, interfaces could be given explicit 
declaratively-empty constructors, which in turn could be given less-public 
access than the interfaces themselves.  This would provide the same level of 
subclass control for interfaces as for abstract classes, with an effect close 
to “sealed interfaces”, from a class-like primitive (access control on 
constructors).  In particular an interface could be sealed to its nest mates by 
marking its declaratively-empty constructor as private (and so on).

I’m not pointing this out to say we should done sealing differently; I love the 
way sealing turned out.  But it’s important to be aware of some underlying 
“class physics” at play here with both interfaces and abstracts.  As a VM guy I 
tend to see the “physics" this way as logical design constraints that bubble up 
from the VM, instead of starting with a desired psychology and working down 
through the chemistry (if you get my drift).

Over time I see interfaces becoming more like abstract classes (notably with 
default methods), and abstract classes returning the favor by growing 
declaratively-empty constructors.  This is not an accident.  I’m convinced that 
as we continue to pay attention to the “physics”, we will be better informed in 
our treatment of other aspects of types, including instance fields and identity.

> (I remain unconvinced that instance fields in inline-friendly abstract 
> classes could possibly be in balance, cost-benefit-wise, and 
> super-unconvinced about inlines extending non-abstract classes.)

My concern here is to point out the logical possibility of such things, not to 
advocate for them now.  Treating fields and non-abstract supers as corollaries, 
rather than than axioms, makes me more certain we have grasped the physical 
essentials of the problem.  This is a valuable design heuristic:  We know that 
if we can say “no” to features while still understanding how they could fit in 
the future, we have arrived at a more factored, more desirable design.  This is 
why I’m talking now, hypothetically, about fields and concretes.

(Which is more important, physics or psychology?  Neither and both, I suppose, 
but physics has this privilege:  If you build on an inconsistent or 
gratuitously complex logical foundation, your user experience will never ever 
be as smooth as it could be.)

> As John says, there are three potential states for an abstract type: 
> always-identity (true of traditional abstract classes), always-inline, and 
> identity-agnostic.  (A possibly way to capture these is by leaning on our new 
> friends IdentityObject and InlineObject; an always-inline abstract type 
> implements InlineObject, and always-identity abstract type implements 
> IdentityObject, and an agnostic one implements neither.)  It is also possible 
> we might prune away the always-inline flavor of abstract types, leaving us 
> with two: inline-friendly or identity-locked.

I think the always-inline flavor of abstract *classes* can be replaced, for
most use cases, with interfaces (with sealing + default methods - toString),
and later on with templates (as long as the polymorphism is parametric).
Many uses of an always-inline flavor of *interfaces* can be replaced by a
sealed interface, where all the permits are inlines.

Dan and Remi have pointed out some places where we might be confronted
with a demand for more, which means we should keep our eyes open.
I’m very happy to bid less, for now, and perhaps forever.

> From a JLS perspective, we could say an abstract type is inline-friendly iff:
>  - its su

Re: Superclasses for inline classes

2019-12-20 Thread Brian Goetz
Stepping back, the thing that frightened us away from this was the 
combination of (a) not wanting to have a modifier on abstract classes to 
indicate inline-friendly, and (b) worrying that it was a lot of 
(brittle) work to structurally detect inline-friendly abstract classes.  
Dan has cut this knot by tying it to the presence of a 
declaratively-empty constructor, which changes the story a lot.  (I 
remain unconvinced that instance fields in inline-friendly abstract 
classes could possibly be in balance, cost-benefit-wise, and 
super-unconvinced about inlines extending non-abstract classes.)


As John says, there are three potential states for an abstract type: 
always-identity (true of traditional abstract classes), always-inline, 
and identity-agnostic.  (A possibly way to capture these is by leaning 
on our new friends IdentityObject and InlineObject; an always-inline 
abstract type implements InlineObject, and always-identity abstract type 
implements IdentityObject, and an agnostic one implements neither.)  It 
is also possible we might prune away the always-inline flavor of 
abstract types, leaving us with two: inline-friendly or identity-locked.


From a JLS perspective, we could say an abstract type is 
inline-friendly iff:

 - its supertypes are all inline-friendly
 - it has a declaratively empty constructor
 - it has no synchronized methods
 - it has no instance fields
 - it has no instance initializers

Object, and all interfaces, would be inline-friendly (we can adjust the 
declaration of Object to meet this requirement); the compiler would 
structurally recognize abstract classes as inline-friendly and set the 
bits in the classfile.


In migrating Integer and friends to be inline-friendly (or 
inline-locked) abstract classes, all we do is push the fields and 
instance method implementations down into the primitive classes, which 
is fine because these classes are final.  (Making Integer an abstract 
class vs an interface means that int inherits the stupid statics on 
Integer, like the terminally confusing getInteger(String).  Maybe some 
further deprecation of static inheritance is warranted here.)


If we say "instance fields is a can of worms" (which I think we should), 
we get a further simplification: we don't need to deal with the 
combination of both declaratively-empty constructors and traditional 
constructors; either you have real constructors (identity-locked) or 
empty ones (inline-locked/friendly), and in the case of an 
inline-friendly being extended by an identity class, the super 
constructor call is seen as a no-op.



So, (3) allow the constructor with no arguments to be declared “abstract”
with no body.  Amend the JVM rules to allow this, and to check upward
to the super for the same condition.  During static checking of source,
treat such a constructor as empty,*and*  as forbidding non-static initializers.
I think that gets what we need in a much clearer manner.


The remaining bikeshed this leaves us is how to mark the constructor 
(abstract seems a good strawman), and whether to pull on the 
{Inline,Identity}Object levers.




On 12/19/2019 9:12 PM, John Rose wrote:

On Dec 18, 2019, at 3:57 PM, Dan Smith  wrote:

[Expanding on and summarizing discussion about abstract superclasses from 
today's meeting.]

-
Motivation

There are some strong incentives for us to support inline classes that have 
superclasses other than Object. Briefly, these include:

- Identity -> inline migration candidates (notably java.lang.Integer) often 
extend abstract classes
- A common refactoring may be to extend an existing class with a (possibly 
private) inline implementation
- Abstract classes are more expressive than interfaces
- If we compile Foo.ref to an abstract class, we can better represent the full 
API of an inline class using an abstract class

I’m glad we are cracking open this can of worms; I’ve always thought
that interfaces as inline supers were good enough but not necessarily
the whole story.

(At the risk of instilling more terror, I’ll say that I think that an abstract
super to an inline could contribute non-static fields, in a way that is
meaningful, useful, and efficient.  The initialization of such inherited
fields would of course use withfield and would require special rules
to allow the initialization to occur in the subclass constructor/factory.
I suppose this is a huge feature, as Dan says later.  A similar effect will
be available from templates, with less special pleading.)

(Does it make sense to allow an abstract class to *also* be inline?
Maybe, although there is a serious question about its default value.
If a type is abstract its default value is probably required to be null.)

A useful organizing concept for abstract supers, relative to inlines,
is a pair of bits, not both true, “always-inlined” and “never-inlined”.
Object and interfaces have neither mark by default.  The super of
an identity class cannot be “always-inlined" and the super of an
inline c

Re: Superclasses for inline classes

2019-12-19 Thread John Rose
On Dec 18, 2019, at 3:57 PM, Dan Smith  wrote:
> 
> [Expanding on and summarizing discussion about abstract superclasses from 
> today's meeting.]
> 
> -
> Motivation
> 
> There are some strong incentives for us to support inline classes that have 
> superclasses other than Object. Briefly, these include:
> 
> - Identity -> inline migration candidates (notably java.lang.Integer) often 
> extend abstract classes
> - A common refactoring may be to extend an existing class with a (possibly 
> private) inline implementation
> - Abstract classes are more expressive than interfaces
> - If we compile Foo.ref to an abstract class, we can better represent the 
> full API of an inline class using an abstract class

I’m glad we are cracking open this can of worms; I’ve always thought
that interfaces as inline supers were good enough but not necessarily
the whole story.  

(At the risk of instilling more terror, I’ll say that I think that an abstract
super to an inline could contribute non-static fields, in a way that is
meaningful, useful, and efficient.  The initialization of such inherited
fields would of course use withfield and would require special rules
to allow the initialization to occur in the subclass constructor/factory.
I suppose this is a huge feature, as Dan says later.  A similar effect will
be available from templates, with less special pleading.)

(Does it make sense to allow an abstract class to *also* be inline?
Maybe, although there is a serious question about its default value.
If a type is abstract its default value is probably required to be null.)

A useful organizing concept for abstract supers, relative to inlines,
is a pair of bits, not both true, “always-inlined” and “never-inlined”.
Object and interfaces have neither mark by default.  The super of
an identity class cannot be “always-inlined" and the super of an
inline class cannot be “never-inlined”.  Or, an identity (resp. inline)
class has the “always-inlined” (resp. “never-inlined”) bit set.  And
for every T <: U in the class hierarchy, if T is always-inlined, then
U must not be never-inlined, and vice versa.  Thus if U is marked
then every T <: U is forbidden to have the opposite mark.  Or,
even more simply, both bits are deemed to inherit down to all
subtypes, and no type may contain both marks.

I don’t know how to derive those bits from surface syntax.  A marker
interface for each is a first cut: AlwaysInlined, NeverInlined.  Marker
interfaces are kind of smelly.  These particular ones work a little better
than their complements (InlineFriendly, IdentityFriendly) because
they exclude options rather than include them.

(Could a *non-abstract* inline be a super of another inline?  No, I’d
like to draw the line there, because that leads to paradoxes with flattening,
or else makes the super non-flattenable in most uses, or violates a
substitutability rule.)

> To be clear, much of this has to do with migration, and I subscribe to a 
> fairly expansive view of how much we should permit and encourage migration. I 
> think most every project in the world has at least a few opportunities to use 
> inline classes. Our design should limit the friction necessary (e.g., 
> disruptive redesigns of type hierarchies) to integrate inline classes with 
> the existing body of code.

You have a point.  Migration is not a task but a way of life?

> We've considered, as an alternative, supporting transparent migration of 
> existing classes to interfaces. But this raises many difficult issues 
> surrounding source, binary, and behavioral compatibility. It would be nice 
> not to have to tackle those issues, nor introduce a lot of caveats into the 
> class -> interface migration story.

As I said earlier, for value types we have this recurring need to bend
interfaces to be more like abstract classes, or else allow abstract classes
to become more like interfaces.

> -
> Constraints
> 
> Inline class instantiation is is fundamentally different from identity class 
> instantiation. While the language seeks to smooth over these differences, 
> under the hood all inline objects come from 'defaultvalue' and 'withfield' 
> invocations. There is no opportunity in these bytecodes for a superclass to 
> execute initialization code.

In the case we are discussing, the interface-like trick that abstract
classes need to learn is to have (declaratively) empty constructors.

I think that if a class (abstract or not) has a non-empty constructor,
it must also be given the “never-inline” mark.  (This is one reason
that mark isn’t simply a marker interface.)  In this way (or some
equivalent) a class with a non-empty constructor will never attempt
to be the super of an inline.

> (Could we redesign the construction model to properly delegate to a 
> superclass? Sure, but that's a huge new feature that probably isn't justified 
> by the use cases.)

Probably not.  Unless folks demand to factor fields as well as behaviors
into abstract supers of inlines.

> As a resu

Re: Superclasses for inline classes

2019-12-19 Thread John Rose
On Dec 18, 2019, at 3:57 PM, Dan Smith  wrote:
> 
> abstract superclasses

A quick observation:  The Panama Vector API uses interfaces to abstract
from specific vector types, and those specific vector types must be made
inline to enable full optimization.

For example: Float128Vector <: FloatVector <: Vector <: Vector

In this scheme we either need to seal FloatVector to Float128Vector
and its siblings, or else we need to do something with abstract classes.
There may be incremental benefits to making FloatVector be an
abstract class, such as being able to define and document toString,
equals, and hashCode.

If in addition we could define FloatVector as an abstract *inline* class,
we might benefit from the effect of FloatVector being non-nullable.
The top-level interface Vector would still be nullable.

The plan of record is to make every vector type be either an interface
or an inline, and then move to specialized interfaces and inlines when
templates become available.  But Vector is one place where abstract
classes are worth a serious look.

— John

Re: Superclasses for inline classes

2019-12-19 Thread Dan Smith
Yes, it is totally on the table that the language model doesn't bother to 
introduce an implicit class or interface above the inline class, and instead 
we're talking about two types derived from a single class declaration.

No matter what choice we make there, there's a separate question of whether 
inline classes can extend other classes, and I'm arguing that there are some 
good reasons that they should.

(There's also the question of how a 'ref' type gets compiled, nevermind what 
the language model says, and whether an abstract class would be a useful tool 
for that purpose.)

> On Dec 19, 2019, at 2:51 AM, Remi Forax  wrote:
> 
> It occurs to me that we may have jump into the conclusion that we should use 
> inheritance/implementation a little to fast.
> At least, i think it worth exploring another complementary option.
> 
> In Java, you have several ways to have subtyping,
> saying a FooInline is a subclass FooInline.ref is one way,
> but using wildcard is another one, if Foo can be a template classes with 
> Foo the inline.
> 
> It will not solve the retrofitting of java.lang.Number so it's a 
> complementary approach but it can solve the retrofitting of java.lang.Integer 
> in a better way than to use subclasses.
> 
> Let say java.lang.Integer is a template class, but not a template class over 
> a type like a specialized generics are but a template class over the inline 
> bit.
> In that case java.lang.Integer can have two specializations, one is an 
> indirect type, the result of new Integer(8), and one is an inline type the 
> result of Integer.valueOf(8).
> 
> The nice properties of this organization are:
> - object.getClass() will return Integer.class for both specializations (it's 
> not the same species but it's the same class).
> - java.lang.Integer can still be final (because final means no subclass but 
> not no subtype).
> 
> Rémi
> 
> 
> 
> 



Re: Superclasses for inline classes

2019-12-19 Thread Remi Forax
It occurs to me that we may have jump into the conclusion that we should use 
inheritance/implementation a little to fast.
At least, i think it worth exploring another complementary option.

In Java, you have several ways to have subtyping,
saying a FooInline is a subclass FooInline.ref is one way,
but using wildcard is another one, if Foo can be a template classes with 
Foo the inline.

It will not solve the retrofitting of java.lang.Number so it's a complementary 
approach but it can solve the retrofitting of java.lang.Integer in a better way 
than to use subclasses.

Let say java.lang.Integer is a template class, but not a template class over a 
type like a specialized generics are but a template class over the inline bit.
In that case java.lang.Integer can have two specializations, one is an indirect 
type, the result of new Integer(8), and one is an inline type the result of 
Integer.valueOf(8).

The nice properties of this organization are:
- object.getClass() will return Integer.class for both specializations (it's 
not the same species but it's the same class).
- java.lang.Integer can still be final (because final means no subclass but not 
no subtype).

Rémi