> On Jan 7, 2020, at 1:30 PM, Brian Goetz <brian.go...@oracle.com> wrote:
> 
> 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;

FWIW, I think it's helpful for case analysis to flatten the list of things that 
can be nested/contain nested things as follows:
- top-level/static classes
- inner classes* (I always forget whether this includes local classes, but 
turns out it does)
- interfaces
- records
- enums
- annotation types
- static methods/initializers
- instance methods/local methods/constructors/instance initializers*

The proposal is that each one of these can be nested in any of these.

Two of these eight are marked with asterisks because their bodies can reference 
enclosing non-static variables and type variables. All the rest are "static 
declarations".

Annotation types currently have some restrictions on methods that probably 
ought to be relaxed for uniformity?

Two things this presentation leaves off of the list: instance/local variables, 
and static variables. These both continue to have ad hoc nesting constraints, 
probably for good reason:
- instance/local variables cannot be nested in interfaces, records, or 
annotation types
- static variables cannot be nested in instance/local methods or static methods

(Some other languages support the second one, which I always find weird; on the 
other hand, once you have static classes nested in methods, you effectively 
have the same thing.)

> 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.)  

Here's the JLS rule, from 6.5.6.1, that we're enhancing:

"If an expression name consists of a single Identifier, then there must be 
exactly one declaration denoting either a local variable, formal parameter, or 
field in scope at the point at which the Identifier occurs. Otherwise, a 
compile-time error occurs."

"If the declaration denotes an instance variable (§8.3.1.1), the expression 
name must appear within an instance method (§8.4.3.2), instance variable 
initializer (§8.3.2), instance initializer (§8.6), or constructor (§8.8). If 
the expression name appears within a class method, class variable initializer, 
or static initializer (§8.7), then a compile-time error occurs."

Could be modified to something like:

"If the declaration denotes a local variable, formal parameter, or instance 
variable, then the expression name must not appear within a static type 
declaration, static method, static field initializer, or static initializer, 
unless that declaration also encloses the variable declaration. Otherwise, a 
compile-time error occurs."

Reply via email to