Towards cleaner nesting

Remi Forax forax at univ-mlv.fr
Tue Jan 7 21:27:43 UTC 2020


Hi Brian, 
Nice sump-up, i like it very much. 

I believe we also need to think about type parameters, they are also impacted by the nesting/static context so should be in NC(X). 

Rémi 

> De: "Brian Goetz" <brian.goetz at oracle.com>
> À: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> Envoyé: Mardi 7 Janvier 2020 21:30:27
> Objet: Towards cleaner nesting

> Everything about nesting in Java is a mess. The terminology is a mess (top level
> classes, nested classes, inner classes, local classes, anonymous classes); the
> set of restrictions on what can nest in what is ad-hoc (can have local classes
> but not local interfaces; inner classes cannot have static members, including
> static nested classes), and the set of rules about what must be, can be, or
> cannot be static is also ad-hoc (nested classes can be static or not, nested
> interfaces are implicitly static, but local and anonymous classes may not be
> static, even though it might make sense.) On top of that, we can nest classes
> in methods (sometimes) and methods in classes but not methods in methods (local
> methods).

> Not only does this make for a lot of accidental complexity in specification,
> implementation, and user's brains, but it means every feature interact with
> this complexity. Nested records are implicitly static, but this meant that in
> 14 we can't have nested records in non-static classes, because, non-static
> classes can't have static members. (Yes, this could be fixed; hold your "why
> don't you just" suggestions.) And we borked up the implementation of local
> records the first time around, where they accidentally capture effectively
> final locals, which they shouldn't -- because we'd never really charted the
> "static local class" territory, and got it wrong the first time. (Yes, this can
> be fixed too, and will be before 14 goes out.)

> So, I'd like to propose a simpler, general story of nesting (which is consistent
> with the ad-hoc rubbish we have) which we can get to in stages. The purpose of
> this mail is to discuss the model; in what increments we get there is a
> separate story.

> Goals:
> - Anything (class, interface, record, enum, method) can be nested in anything;
> - Some things are always static (enums, records, interfaces) when nested; the
> rest can be made static when desired;
> - The rule about "no static members in nonstatic nested classes" has to go;
> - Rules about whether members / locals from enclosing contexts can be specified
> in a single place, using local reasoning.

> The core of this is coming to an understanding of what "static" means. When
> construct X nests in Y (whether X and Y are classes, methods, interfaces, etc),
> for "X" to be "static" means that nesting is being used purely for purposes of
> namespacing, and not for purposes of having access to names (locals or
> nonstatic class members) from enclosing constructs.

> Unfortunately all the terms we might use for whether or not a symbol in an outer
> construct can be used in a nested construct -- such as "accessible" -- are
> overloaded with other meanings. For purposes of this discussion, let's call
> this "capturable" (this is also overloaded, but less so.) Each construct (class
> type or method) has two sets of names from outer constructs that are capturable
> -- a _statically capturable_ set SC(X), and a _non-statically capturable_ set
> NC(X). We can define capturability using local reasoning:

> Base cases:
> - Names of static members in X are in SC(X);
> - Names of instance members of X (if X is a class) or effectively final locals
> of X (if X is a method) are in NC(X);

> Induction cases, where X is nested directly in Y:
> - SC(Y) is in SC(X)
> - If _X is not static_, then NC(Y) is in NC(X)

> We then say that X can capture names in SC(X) and NC(X); all we need to compute
> capturability is the capture sets of X's immediately enclosing construct, and
> whether X is static or not in that construct (modulo shadowing etc.)

> For the math-challenged, what this means is:
> - A nested construct can access static members of all the enclosing constructs;
> - A nested non-static construct can access instance members and effectively
> final locals of all enclosing constructs, up until we hit a static construct,
> and then capturing stops. (So if Z is nested in Y is nested in static X, Z can
> access instance members / eff final locals of Y and X but not anything
> non-static from outside of X.)

> Note that this is consistent with what currently happens when X is a method as
> well as a class type; static methods in a class "capture" the static members of
> the enclosing class, and instance methods also capture the instance members of
> the enclosing class -- and also consistent with capturing in lambdas and
> anonymous classes, if we assume that these are always non-static constructs.

> We then say enums, records, and interfaces are _always_ static when nested,
> whether declared so or not, we eliminate the restriction about static members
> in non-static nested classes (now that we have a clear semantics for them), and
> allow local classes to be declared as static. (Eventually, we also relax the
> restrictions about methods in methods, static or not.)

> (Additionally, the model supports the notion of "static lambda" and "static
> anonymous class" with obvious semantics (can't capture anything); we can decide
> later whether adding this flexibility is worth the additional surface syntax.)

> This is a strict superset of the status quo, and yields a more flexible and
> regular language -- and hopefully a simpler spec (since so many of these cases
> are specified as ad-hoc corner cases.)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20200107/3aa7b39c/attachment-0001.htm>


More information about the amber-spec-experts mailing list