-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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
SI-9393 fix modifiers of ClassBTypes for Java annotations #4638
Conversation
1c8c92a
to
00bbca9
Compare
review by @retronym. opinions welcome: is it too much of a workaround? |
195cd07
to
573a63e
Compare
I'm fine with the approach of repairing this in the backend for now, but agree that a special case in the typer would be worth trying. I'd like to see your (overdue) commentary surrounding flags turned into a unit test, exercising this sort of thing:
It might also be worth noting that the difference between anti and late flags is that the former allow a transition from |
*assertions |
I would think we should expose the new flag in |
Given that we should never pickle this new flag (it is only on external symbols), we don't need to update the pickle format or But we probably should update |
@@ -120,7 +120,8 @@ class ModifierFlags { | |||
final val ARTIFACT = 1L << 46 // symbol should be ignored when typechecking; will be marked ACC_SYNTHETIC in bytecode | |||
// to see which symbols are marked as ARTIFACT, see scaladocs for FlagValues.ARTIFACT | |||
final val DEFAULTMETHOD = 1L << 47 // symbol is a java default method | |||
final val ENUM = 1L << 48 // symbol is an enum | |||
final val ENUM = 1L << 48 // symbol is a java enum | |||
final val ANNOTATION = 1L << 49 // symbol is a java annotation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should call this JAVA_ANNOTATION
. Otherwise it might be surprising to find it absent from Scala defined annotations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I ever find the issue with typechecking Java annotation code correctly, Scala will also be able to emit platform annotations. I think it's not simply a question of Scala-defined vs. Java-defined annotations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i don't fully get what you are saying. in any case, emitting java annotations also needs work in the bytecode generator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, it's just the part I'm still blocked on, and I lacked the time to solve this issue.
Just looked around a bit, there's a comment in that file:
|
36a81ea
to
0566ed6
Compare
re-review by @retronym - it grew quite a bit... |
@@ -93,7 +93,7 @@ trait Parsers { self: Quasiquotes => | |||
import treeBuilder.{global => _, unit => _} | |||
|
|||
// q"def foo($x)" | |||
override def param(owner: Name, implicitmod: Int, caseParam: Boolean): ValDef = | |||
override def param(owner: Name, implicitmod: Long, caseParam: Boolean): ValDef = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After this change, the subsequent implicitMod.toLong
is redundant.
I've removed the redundant |
assertTrue(withFlagMask(AllFlags)(sym.setFlag(AllFlags).flags) == allButNegatable) | ||
assertTrue(withFlagMask(AllFlags)(sym.setFlag(allButLateable).flags) == allButNegatable) | ||
|
||
assertTrue(withFlagMask(AllFlags)(sym.setFlag(lateFlags).flags) == (lateFlags | lateable)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer assertEquals
to assertTrue
for better assertion failure messages.
This will result in slightly less concise bytecode for places referring to the formerly integer constants. Longs are always loaded from the constant pool with a % cat sandbox/test.scala && scalac sandbox/test.scala && javap -classpath . -c Test
class Test {
final val i1 = 42
final val i2 = 999
final val i3 = Int.MaxValue
final val l1 = 42L
def test = {
java.lang.Integer.signum(i1)
java.lang.Integer.signum(i2)
java.lang.Integer.signum(i3)
java.lang.Long.signum(l1)
}
}
Compiled from "test.scala"
public class Test {
public final int i1();
Code:
0: bipush 42
2: ireturn
public final int i2();
Code:
0: sipush 999
3: ireturn
public final int i3();
Code:
0: ldc #18 // int 2147483647
2: ireturn
public final long l1();
Code:
0: ldc2_w #20 // long 42l
3: lreturn
public int test();
Code:
0: bipush 42
2: invokestatic #28 // Method java/lang/Integer.signum:(I)I
5: pop
6: sipush 999
9: invokestatic #28 // Method java/lang/Integer.signum:(I)I
12: pop
13: ldc #18 // int 2147483647
15: invokestatic #28 // Method java/lang/Integer.signum:(I)I
18: pop
19: ldc2_w #20 // long 42l
22: invokestatic #33 // Method java/lang/Long.signum:(J)I
25: ireturn
public Test();
Code:
0: aload_0
1: invokespecial #37 // Method java/lang/Object."<init>":()V
4: return
} Does this matter? Maybe not. But we should sanity check performance before we do this, and also structure things so that we could revert just this change without losing any correctness that it brought. |
2cfbda9
to
60fd361
Compare
The Scala classfile and java source parsers make Java annotation classes (which are actually interfaces at the classfile level) look like Scala annotation classes: - the INTERFACE / ABSTRACT flags are not added - scala.annotation.Annotation is added as superclass - scala.annotation.ClassfileAnnotation is added as interface This makes type-checking @annot uniform, whether it is defined in Java or Scala. This is a hack that leads to various bugs (SI-9393, SI-9400). Instead the type-checking should be special-cased for Java annotations. This commit fixes SI-9393 and a part of SI-9400, but it's still easy to generate invalid classfiles. Restores the assertions that were disabled in scala#4621. I'd like to leave these assertions in: they are valuable and helped uncovering the issue being fixed here. A new flag JAVA_ANNOTATION is introduced for Java annotation ClassSymbols, similar to the existing ENUM flag. When building ClassBTypes for Java annotations, the flags, superclass and interfaces are recovered to represent the situation in the classfile. Cleans up and documents the flags space in the area of "late" and "anti" flags. The test for SI-9393 is extended to test both the classfile and the java source parser.
Similar to the new JAVA_ANNOTATION flag, be more explicit about flags for java entities.
Adds query methods to the public reflection API for querying the JAVA_ENUM and JAVA_ANNOTATION flags. Didn't include JAVA_DEFAULTMETHOD because it does not correspond to a real java classfile flag (just a non-abstract method in an interface), and we want to clean the usage of this flag before adding it to a public API. The flags themselfs are not added to the reflection API. A comment in api/FlagSets.scala says: Q: I have a pretty flag. Can I put it here? A: Only if there's a tree that cannot be built without it. If you want to put a flag here so that it can be tested against, introduce an `isXXX` method in one of the api.Symbols classes instead.
Thanks for the review and patches! I included your changes to remove more I'll run some a/b testing to see if I can find any performance difference. In any case, the last commit doesn't fix any bugs, it can be reverted. |
Some compiler timings for the flags-as-long change. Compiling the standard library ten times:
Compiling List.scala ten times
https://docs.google.com/spreadsheets/d/12-GILZectCueVLeyC0HjOlnlQRZPBcTADGf9sVmGAW0/edit#gid=0 |
I'd say that difference is within measurement error. But maybe we should leave that commit aside and use it to motivate us to get at least a small JMH compiler benchmark suite up and running to be able to be more certain. |
I also noticed that constant folding doesn't work in cases like |
OK, removed the last commit. |
LGTM |
SI-9393 fix modifiers of ClassBTypes for Java annotations
The Scala classfile and java source parsers make Java annotation
classes (which are actually interfaces at the classfile level) look
like Scala annotation classes:
This makes type-checking @annot uniform, whether it is defined in Java
or Scala.
This is a hack that leads to various bugs (SI-9393, SI-9400). Instead
the type-checking should be special-cased for Java annotations.
This commit fixes SI-9393 and a part of SI-9400, but it's still easy
to generate invalid classfiles. Restores the annotations that were
disabled in #4621. I'd like to leave these assertions in: they
are valuable and helped uncovering the issue being fixed here.
A new flag ANNOTATION is introduced for Java annotation ClassSymbols,
similar to the existing ENUM flag. When building ClassBTypes for
Java annotations, the flags, superclass and interfaces are recovered
to represent the situation in the classfile.
Cleans up and documents the flags space in the area of "late" and
"anti" flags.
The test for SI-9393 is extended to test both the classfile and the
java source parser.