8000 SI-9013 SI-1459 Inherit @varargs annotations by adriaanm · Pull Request #5631 · scala/scala · GitHub
[go: up one dir, main page]

Skip to content

SI-9013 SI-1459 Inherit @varargs annotations #5631

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 2 commits into from
Jan 27, 2017

Conversation

adriaanm
Copy link
Contributor

This is needed if an abstract method is annotated this way.
In addition to the abstract varargs forwarder that we already emit,
we have to emit a concrete forwarder in the subclass.

@scala-jenkins scala-jenkins added this to the 2.11.9 milestone Jan 10, 2017
@adriaanm
Copy link
Contributor Author

Review by @retronym (for the part he didn't author ;-))

// causing `super.exclude` to veto it from our bridge generation here, unless we explicitly include it (hence `!sym.hasFlag(VBRIDGE) &&`).
// TODO: could we generate just one bridge method that does both the java-vararg-array <-> scala-repeated-param-seq conversion
// as well as the casts from the erased types of the overridden generic method (regular bridge method)?
override def exclude(sym: Symbol) = !sym.isMethod || (!sym.hasFlag(VBRIDGE) && super.exclude(sym))
Copy link
Member

Choose a reason for hiding this comment

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

The overloaded meaning of the ARTIFACT flag (ACC_SYNTHETIC in bytecode, affects overriding pairs) is surprising, I wasn't aware of that. Without knowing the details, it seems that more explicit checks would be better in places where isArtifact impacts type-checking (the doc comment on ARTIFACT says "should be ignored when typechecking" - what ever that means).

Maybe instead of this fix here, we could skip the ARTIFACT flag for VBRIDGEs? But that's probably a bad idea in 2.11.x, as it changes the bytecode (ACC_SYNTHETIC).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think artifacts are intended to be skipped while type checking (for performance? because they may have incorrect types? because their types are trivially correct?)

@@ -811,7 +811,7 @@ abstract class UnCurry extends InfoTransform
}

// check if the method with that name and those arguments already exists in the template
enteringUncurry(currentClass.info.member(forwSym.name).alternatives.find(s => s != forwSym && s.tpe.matches(forwSym.tpe))) match {
enteringUncurry(currentClass.info.member(forwSym.name).filter(s => s != forwSym && s.tpe.matches(forwSym.tpe)).alternatives.headOption) match {
Copy link
Member

Choose a reason for hiding this comment

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

I don't understand how this fixes the bug - why are the two versions not the same?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right -- the tests pass with both variations. I'll revert the code change.

@adriaanm adriaanm requested a review from retronym January 20, 2017 00:28
@retronym
Copy link
Member

I wonder if we need a corresponding change (special case for ARTIFACT / VBRIDGE) in checkNoDoubleDefs.

What behaviour changes if we remove the || sym.isArtifact condition from OverridingPairs#Cursor.exclude altogether?

@adriaanm
Copy link
Contributor Author

What behaviour changes if we remove the || sym.isArtifact condition from OverridingPairs#Cursor.exclude altogether?

I'd prefer to delay this experiment to 2.12 or 2.13. For 2.11, I've refactored a bit to make the choice a more explicit one.

@retronym
Copy link
Member
retronym commented Jan 24, 2017

I think artifacts are intended to be skipped while type checking (for performance? because they may have incorrect types? because their types are trivially correct?)

ACC_SYNTHETIC is described a little vaguely as "does not exist in source code". More precisely, it is used to mark a member that does not exist in source code and cannot be called from source code.

Examples in javac:

  public static java.lang.annotation.RetentionPolicy valueOf(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/annotation/RetentionPolicy;
    flags: ACC_PUBLIC, ACC_STATIC

This method is synthesized by javac because of the enum keyword, but it can be called from user code, so is not marked ACC_SYNTHETIC.

class Test {
	private Object x;
	class I
8000
nner {
		{ x.toString(); }
	}
}
  static java.lang.Object access$000(Test);
    descriptor: (LTest;)Ljava/lang/Object;
    flags: ACC_STATIC, ACC_SYNTHETIC

Static accessors are only called by the compiler on behalf of the user, so are marked ACC_SYNTHETIC.

Another example:

abstract class Test implements java.util.function.Function<String, String> {
	abstract public String apply(String s);
}
  public abstract java.lang.String apply(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_ABSTRACT

  public java.lang.Object apply(java.lang.Object);
    descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
    flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC

So javac marks generic bridges are ACC_SYNTHETIC, too. Failure to hide this method from client code would make this look like an overload.

scalac emits this flag for the benefit of Java clients. AFAIK, it has no semantics to the VM itself.

When typechecking calls to Scala-defined classes, we use the ARTIFACT flag (as stored in the ScalaSignature) rather than the ACC_SYNTHETIC flag in the classfile. This would also serve to hide such methods from user code. In the case of generic bridges, however, we hide them from user code in a different way: the class info is pickled at a phase before it has them added.

@retronym
Copy link
Member

Finally, we also have the SYNTHETIC flag, which means "was not defined in source code".


scala> case class C()
defined class C

scala> typeOf[C].decls.filter(_.isSynthetic)
res6: $r.intp.global.Scope = Scopes.Scope(method copy, method productPrefix, method productArity, method productElement, method productIterator, method canEqual, method hashCode, method toString, method equals)

This does not prevent calls to these members.

@retronym
Copy link
Member

Finally (part deux)

I remembered SI-9482 that our typechecker actually does not honour ACC_SYNTHETIC for Java defined classes. I wasn't able to fix that on my first attempt.

retronym and others added 2 commits January 24, 2017 16:33
A Scala method that implements a generic, Java-defined
varargs method, needs two bridges:
  - to convert the collections for the repeated parameters (VBRIDGE)
  - to bridge the generics gap (BRIDGE)

Refchecks emits the varargs "bridges", and erasure takes care
of the other gap. Because a VBRIDGE was also an ARTIFACT,
it was wrongly considered inert with respect to erasure,
because `OverridingPairs` by default excluded artifacts.

Removed the artifact flag from those VBRIDGES, so that they
qualify for a real bridge. It would also work to include
VBRIDGE methods that are artifacts in BridgesCursor.
@adriaanm
Copy link
Contributor Author

I've pushed a different solution, which simply does not add the ARTIFACT flag, just VBRIDGE

@adriaanm
Copy link
Contributor Author

@retronym, @lrytz, does this approach look better to you?

@lrytz
Copy link
Member
lrytz commented Jan 25, 2017

LGTM, I like this approach much more.

8000

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.

5 participants
0