Skip to content

Conversation

adriaanm
Copy link
Contributor

@adriaanm adriaanm commented Nov 30, 2016

This likely regressed in #5294.

Fixes scala/scala-dev#213

Review by @retronym

@adriaanm adriaanm added this to the 2.12.1 milestone Nov 30, 2016
@retronym
Copy link
Member

retronym commented Dec 1, 2016

Not sure we've got this sorted yet. Here's a test case for combinations of trait defined/class defined/trait-mixed in members; val/var/lazy val; field/getter/setter meta annotations that shows the difference between 2.11.8, 2.12.0, and 2.12.1 (as proposed in this PR).

There are still some problems:

  • A field targeted annotation on a class member lazy val ends up on both the field and the getter.
  • A field targeted annotation on a trait member lazy val is lost (not propagated to the field in the class that mixes in the trait)
  • The static accessor method of a getter in a trait contain the same list of annotations as the method it accesses. The same probably occurs for regular concrete trait methods.

@adriaanm
Copy link
Contributor Author

adriaanm commented Dec 1, 2016

Thanks for the test case -- where did it go, though?

@adriaanm
Copy link
Contributor Author

adriaanm commented Dec 1, 2016

I think I have fixes for the first two. Not sure about the static accessors yet

@retronym
Copy link
Member

retronym commented Dec 1, 2016

https://gist.github.com/retronym/c46af6372d325a8bdd1d8cf1315eccf5

Static accessors are spawned in the backend using

final def mkStatic(orig: DefDef, newName: Name, maybeClone: Symbol => Symbol): DefDef = {

@adriaanm
Copy link
Contributor Author

adriaanm commented Dec 1, 2016

Thanks. So you're saying we should just drop all annotations from the static accessor?

@adriaanm
Copy link
Contributor Author

adriaanm commented Dec 1, 2016

I'll incorporate your test tomorrow as well as the logic for the static accessor annotation triage

@retronym
Copy link
Member

retronym commented Dec 1, 2016

Yes, I think we should drop them.

// If it doesn't, `writeObject` will fail to serialize the field `notSerialized`, because `NotSerializable` is not serializable
object Test {
def main(args: Array[String]): Unit =
new java.io.ObjectOutputStream(new java.io.ByteArrayOutputStream) writeObject (new SerializableBecauseTransient)
Copy link

Choose a reason for hiding this comment

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

Shouldn't the lazy vals be forced before trying to serialize? Otherwise the backing field will be null, and serialization will succeed even if it is not transient, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. Thanks!

@adriaanm
Copy link
Contributor Author

adriaanm commented Dec 2, 2016

One test failure:

!!  201 - run/junitForwarders                       [non-zero exit code]
##### Log file '/home/jenkins/workspace/scala-2.12.x-validate-test@2/test/files/run/junitForwarders-run.log' from failed test #####

java.lang.AssertionError: assertion failed: found: $init$ - ;foo - @org.junit.Test();foo$ - 
expected: $init$ - ;foo - @org.junit.Test();foo$ - @org.junit.Test()
	at Test$.check(C_1.scala:10)

@adriaanm
Copy link
Contributor Author

adriaanm commented Dec 3, 2016

Updated test, since it had a nice TODO message that confirmed it needed updating :-) thanks, @lrytz

@dwijnand
Copy link
Member

dwijnand commented Dec 3, 2016

It's green now

@retronym
Copy link
Member

retronym commented Dec 4, 2016

LGTM, tricky stuff!

@adriaanm
Copy link
Contributor Author

adriaanm commented Dec 5, 2016

If I understand @lrytz's comment at scala/scala-dev#213 (comment) correctly, I should drop the withoutAnnotations for static accessors. I think we should either drop all annotations or we keep them all. If we have to keep @inline, someone may be able to make a similar case for other annotations.

Alternatively, could we avoid looking at annotations of static accessors in the inliner? Could the inliner see through the static accessor and decide inlining based on the target of the accessor? inlineRequest is already special cased on isTraitSuperAccessor to use findSingleCall, but I didn't look deeper than that.

This likely regressed in scala#5294.

Review feedback from retronym:
 - Tie annotation triaging a bit closer together

durban kindly provided initial version of test/files/run/t10075.scala
And pointed out you must force `lazy val`, since `null`-valued field
is serializable regardless of its type.

Test test/files/run/t10075b courtesy of retronym
Based on review suggestion by retronym.

See also scala/scala-dev#213
@lrytz
Copy link
Member

lrytz commented Dec 5, 2016

It turns out the inliner does not rely on the @inline annotation on static super accessors.

For every member method, we store some metadata in the ClassBType (The MethodInlineInfo, it contains is information that can only be obtained from a method symbol, but, without unpickling, not from the classfile signature. Example: is the method annotated @inline. This information is added to the class as a custom attribute and used in case the inliner pulls in a new classfile.)

When building a ClassBType, the member method symbols are obtained from the classSym.info.decls. However, the static accessors are not entered into the class scope, so they don't show up. That's why there's a special case that creates additional MethodInlineInfos for static accessors (

if (needsStaticImplMethod(methodSym)) {
val staticName = traitSuperAccessorName(methodSym).toString
val selfParam = methodSym.newSyntheticValueParam(methodSym.owner.typeConstructor, nme.SELF)
val staticMethodType = methodSym.info match {
case mt @ MethodType(params, res) => copyMethodType(mt, selfParam :: params, res)
}
val staticMethodSignature = staticName + methodBTypeFromMethodType(staticMethodType, isConstructor = false)
val staticMethodInfo = MethodInlineInfo(
effectivelyFinal = true,
annotatedInline = info.annotatedInline,
annotatedNoInline = info.annotatedNoInline)
). The annotatedInline bit is taken from the corresponding member method.

@adriaanm
Copy link
Contributor Author

adriaanm commented Dec 5, 2016

Aha! Thanks for explaining. So this is ready to go as far as you're concerned?

@lrytz
Copy link
Member

lrytz commented Dec 5, 2016

yes, LGTM

@adriaanm
Copy link
Contributor Author

adriaanm commented Dec 5, 2016

Cool. When I split up my old commit in two, I missed one test case. The last commit is green, so I'm going to merge. One broken test isn't going to break future git bisecting.

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

Successfully merging this pull request may close these issues.

6 participants