Design decisions: forward reference, corralling, method keys

Robert Field robert.field at oracle.com
Mon May 18 17:04:48 UTC 2015


Resend...


On May 13, 2015 5:54:11 PM Robert Field <robert.field at oracle.com> wrote:

> Feedback needed, read on...
>
> The original design decision, see the JEP, was that methods bodies     and, 
> within classes, method bodies and field initializers could have     forward 
> references.  This kind of forward reference is handled by     corralling 
> the method or initializer -- meaning that the new     definitions with 
> unresolved references can be referenced by other     code without error or 
> unresolved reference. Then at runtime,     attempts to execute the 
> corralled code causes an UnresolvedException     .
>
> However, I've heard loud complaint that only this is too     restrictive.  
> That forward reference in signatures is important.      That, for example, 
> it is important to be able to be able to paste     definitions from the 
> body of a class in the order they appear. That:
>
> class B extends A {}
> class A {}
>
> should be legal.
>
> This motivation is well founded, and there are other correlated     
> changes/observations that align with this, that an existing     declaration 
> can have signature failures on update or that the above     can be declared 
> first A then B, but then A can be dropped, and that     these motivated the 
> introduction of JShellState.Status (below).
>
> Thus, new definitions which cannot be corralled (ActiveCorralled)     can 
> be put in ActiveFailed status, so that, unlike corralled, there     is no 
> definition defined,so it can't be used by other input without     error 
> (which, depending on context, may be corralled or failed).      However, it 
> is still active which means on update it can become     active. As an 
> example from the tool:
>
> -> class B extends A {}
> |  Error:
> |  cannot find symbol
> |    symbol:   class A
> |  class B extends A {}
> |                  ^
> |  Added class B, however, it cannot be referenced until         the above 
> errors are resolved
>
> -> class A {}
> |  Added class A
>
> -> new B()
> |  Expression value is: B at 5ecddf8f
> |    assigned to temporary variable $1 of type B
> This can and should be made prettier by reporting the unresolved     inline 
> in the message (similarly to the corralled case), but it     gives the idea.
>
> But using this new status (ActiveFailed) widens the envelope and     
> introduces some new issues, two are known so far:
>
> (A) Thanks, Andrei, for pointing this out.  If a variable with     
> initializer is in ActiveFailed an update can move it to Active.      What 
> is the value of the variable?  Should the initializer be run?      If the 
> initializer was the location of the unresolved, it seems     silly not to 
> run it.  Yet, this would be running arbitrary code on     update which is 
> sure to have surprising behavior in some cases.      Here are the not so 
> perfect options as I see them:
>
> Disallow forward reference in variables. This violates the           
> desired "paste from class body" thinking.        Disallow forward reference 
> in variables with initializers.           This still violates "paste from 
> class body", and it is           strangely asymmetric.        Give the 
> variable the default value which is what is the           currently 
> specified behavior for Replaced updates to a           variable, but it is 
> odd that the initializer never gets           executed, and what if it was 
> the cause of the unresolved?        Run the initializer at update time.  In 
> some cases this           could cause output, hangs, or arbitrary 
> side-effects.            Choosing this would require re-examining execution 
> on update           in all cases.        Use RejectedFailed if the 
> initializer has unresolved           references.  Otherwise (variable type 
> unresolved) run the           initializer and store the result on update 
> when update turns           it active.  This has many problems including 
> the initializer           result type would in almost all cases need to be 
> the variable           type (which is unresolved) or null.
> (B) The key for classes and variables is determined by just their     name. 
>  But because of overloading, for methods, the key is the name     plus the 
> parameter types.  This is fine if the method signature can     be compiled 
> (the body can be stubbed-out as is the case when     corralled), as the 
> full qualified name of the parameter types is     then known.  But if one 
> of the parameter types is unresolved its     full name is not known either 
> so the correct key cannot be     determined.  This is significant because 
> if it is put in     ActiveFailed it can updated into Active (or 
> ActiveCorralled) at     which point its actual key is then known and a 
> central invariant is     that the key for an input does not change,  And 
> here is another set     of non-ideal options that I can think for this:
>
> Disallow forward reference on method parameter types. This           
> violates the desired "paste from class body" thinking.        Assume any 
> unresolved reference in parameter types to be a           top-level REPL 
> type.  That is, that it will be defined by an           entered class 
> definition.  If that name gets defined by an           import it won't see 
> it.        Change the definition of key so that it is defined by the        
>    name and UNqualified type names of the parameters.  This           
> should work cleanly but it breaks compatibility with Java           
> semantics (something we trying very hard not to do), if only           in a 
> very small way -- because it would mean that given a           defined 
> method m(foo.Bar x), entering m(baz.Bar x) would           replace rather 
> than overload m(foo.Bar x).
> Throw away the invariant.  Modify the key, oh the issues           with 
> that! Or mark the key dropped on resolving update adding           a new key..
> Reasoned opinions and options I did not consider hereby solicited.
>
> Thanks,
> Robert
>
> public static enum JShellState.Status            Enum Constant and Description
> Active
> Active: a snippet which is in valid form               and can be used by 
> new snippets (if a declaration or               import), and can be 
> executed (if it is executable).
> ActiveCorralled
> ActiveCorralled: a declaration snippet with               unresolved 
> references or other issues.
> ActiveFailed
> ActiveFailed: a declaration snippet failed               compilation on 
> update in a way that cannot be corralled --               signature failure.
> Dropped
> Dropped: a declaration snippet was               explicitly dropped by a 
> call to the JShellState.drop().
> RejectedFailed
> RejectedFailed: a snippet failed               compilation on initial 
> evaluation.
>


More information about the kulla-dev mailing list