Skip to content

Add @static SIP #491

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 25, 2016
Merged

Add @static SIP #491

merged 4 commits into from
Nov 25, 2016

Conversation

DarkDimius
Copy link
Contributor

Add @static SIP

{% endhighlight %}
```

## Comparisson with mirror classes ##
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparison

DarkDimius added a commit to dotty-staging/dotty that referenced this pull request Mar 7, 2016
This pull request implements most of machinery needed for
scala/docs.scala-lang#491

Only 3-rd check is not implemented by this commit.
I propose to get this in faster to fix scala#1149
DarkDimius added a commit to dotty-staging/dotty that referenced this pull request Mar 7, 2016
This pull request implements most of machinery needed for
scala/docs.scala-lang#491

Only 3-rd check is not implemented by this commit.
I propose to get this in faster to fix scala#1149
@DarkDimius
Copy link
Contributor Author

The implementation was merged into Dotty.


For example, classes extending `android.os.Parcelable` are required to have a static field named `CREATOR` of type `android.os.Parcelable$Creator`.

Another example is using an [`AtomicReferenceFieldUpdater`](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.html).
Copy link
Member

@janekdb janekdb Apr 20, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/7/8/ ?

@kjsingh
Copy link

kjsingh commented Nov 24, 2016

why can't we have static as a keyword?

@sjrd
Copy link
Member

sjrd commented Nov 24, 2016

Because adding new keywords breaks existing code that used them as identifiers. Adding keywords must be done very sparingly, only when there is no other option.

@kjsingh
Copy link

kjsingh commented Nov 24, 2016

I guess libs doing interop with Java code will be avoiding that.

@jvican
Copy link
Member

jvican commented Nov 24, 2016

@DarkDimius is there any change you want to do here before the SIP review?

@DarkDimius
Copy link
Contributor Author

I guess libs doing interop with Java code will be avoiding that.

Why would they? You may want some data to be seen as Java static field, and you can use @static for this.

@DarkDimius
Copy link
Contributor Author

@DarkDimius is there any change you want to do here before the SIP review?

@jvican, I've applied those changes now and cleared git history. Feel free to merge if it looks good.

@jvican
Copy link
Member

jvican commented Nov 25, 2016

Great changes, let's merge it for next week's discussion.

@jvican jvican merged commit 53f10e5 into scala:master Nov 25, 2016
@xuwei-k
Copy link
Contributor

xuwei-k commented Nov 28, 2016

#635

@adriaanm
Copy link
Contributor

  • I don't think it's a good idea to let code gen depend in non-obvious ways on an annotation. Can we break this down further? How about....
    • Always emit object members as class statics, as suggested by Martin (solves initialization mismatch that killed Implement @static annotation on singleton object fields. scala#894, and keeps static members out of class, which would be problematic in our type system)
    • Deal with name clashes using an @exportedName annotation?
    • Allow emitting a public static field without accessor using @suppressAccessors?

The proposal should go into more detail on:

  • What about binary compat (adding/removing @static annotation)
  • Interaction with initialization semantics?

Some relevant bugs:

@retronym
Copy link
Member

retronym commented Feb 15, 2017

I'd like to see the "Compilation scheme" section fleshed out. Currently, it talks about how this would fit into the compiler pipeline, but lacks details about how things would be compiled to bytecode.

Here's an example I'm interested in:

package p1;  object C { @static val foo: AnyRef = C; val bar: AnyRef = C }

If I translate this according to the intuitive description herein, taking a few guesses at the details, I start with:

public class Test {
  public static void main(String[] args) {
    System.out.println(C$.MODULE$.foo()); // null
  }
}

class C {
  public static final Object foo;
  static {
    foo = C$.MODULE$; // initializer moved to CompanionClass.<clinit>
  }
}
final class C$ {
  public static C$ MODULE$;
  private final Object bar;
  public static Object foo() { return C.foo; }
  static {
     Object x = C.foo; // or UNSAFE.ensureClassInitialized(classOf[C])
     new C$();
  }
  private C$() {
    super();
    MODULE$ = this;
    bar = this;
  }
}

Running this prints null. I guess we should move the call to C.<init> after the super constructor call and the assignment of the module var:

final class C$ {
  public static C$ MODULE$;
  private final Object bar;
  public static Object foo() { return C.foo; }
  static {
     new C$();
  }
  private C$() {
    super();
    MODULE$ = this;
    Object x = C.foo; // or UNSAFE.ensureClassInitialized(classOf[C])
    bar = this;
  }
}

Which avoids the NPE.

But in that case, triggering C.<clinit> before C$.<clinit> would reverse the order that the initalizer for foo and the super constructor of the module class is run.

Both of these variations have another drawback: References to C in the RHS of static initializers introduce a cyclic dependency between C.<clinit> and C$.<clinit>. This is deadlock prone if a a pair of threads simultaneously statically initialize the class and module class.

So maybe we instead should put leave all the initalization code in the module class?

class C {
  public static Object foo; // can't be final
}
final class C$ {
  public static C$ MODULE$;
  private final Object bar;
  public static Object foo() { return C.foo; }
  static {
     new C$();
  }
  private C$() {
    super();
    MODULE$ = this;
    C.foo = this;
    bar = this;
  }
}

This looks more promising for thread-safe initialization, and preserving semantics. But we are no unable to declare foo as final, which is a bit of a drag, because one reason you want to make a field static is to hold a method handle "constant", and hotspot only is able to fully inline invocations through that handle if the field is marked final. (Or, if it has the JDK internal annotation, java.lang.invoke.Stable, which isn't in our box of tricks.)

@lrytz
Copy link
Member

lrytz commented Feb 15, 2017

We could also consider omitting the accessor methods for @static fields and emitting direct accesses to the field.

@DarkDimius
Copy link
Contributor Author

class C {
  public static Object foo;
  static {
    foo = C$.MODULE$;
  }
}
final class C$ {
  public static C$ MODULE$;
  private final Object bar;
  public static Object foo() { return C.foo; }
  static {
     new C$();
  }
  private C$() {
    super();
    MODULE$ = this;
    Object x = C.foo; // or UNSAFE.ensureClassInitialized(classOf[C])
    bar = this;
  }
}

Is the proposed desugaring.

@retronym

I guess we should move the call to C.<init> after the super constructor call and the assignment of the module var:

I guess you've meant C.<clinit>. Than that's true.

But in that case, triggering C.<clinit> before C$.<clinit> would reverse the order that the initalizer for foo and the super constructor of the module class is run.

C$.<clinit> isn't user defined. Did you mean C$.<init>?

In case you did, the order in which initializer for foo and the super constructor of the module class is run indeed may change under the proposed sip. It's discussed here https://github.com/scala/scala.github.com/pull/658/files#diff-9284321b7900073f358a16a971c06881R159 , do you want me to expand this discussion?

This is deadlock prone if a a pair of threads simultaneously statically initialize the class and module class.

If we always trigger initialization of C.<clinit> from C$.<init> we will introduce ordering here that should help. But in case user-code inherently has cycles in inititaliziation, I don't think we can do anything.

Thanks for the questions, I'll expand the "Compilation scheme" section.

@retronym
Copy link
Member

do you want me to expand this discussion?

Yes, it would be useful to talk about the super constructor thing explicitly,
perhaps with an example showing how the order could vary depending on how
whether the class or module was initialized first.

If we always trigger initialization of C. from C$. we will
introduce ordering here that should help. But in case user-code inherently
has cycles in inititaliziation, I don't think we can do anything.

I still think that we're introducing a new form of cycle by moving
the code into 'C$'-s static initializer, but I guess that's what the user
is asking for with this annotation, so we can argue that it is the user's
responsibility to code around this possibility.

```java
{% highlight java %}
class Foo {
public static int x = 5;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be final?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.