Skip to content

Fix #3476: Add Artifact flag to closures #3503

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 6 commits into from
Apr 10, 2018
Merged

Conversation

Duhemm
Copy link
Contributor

@Duhemm Duhemm commented Nov 18, 2017

@smarter
Copy link
Member

smarter commented Nov 19, 2017

That should indeed fix #3476 but why was the generic signature here wrong in the first place? Couldn't the same issue exist for non-closures? Also, could -Xverify-signature be improved to detect this kind of issues ?

@Duhemm
Copy link
Contributor Author

Duhemm commented Nov 20, 2017

I think that the reason why the signatures are wrong for those nodes is because they are transformed again after erasure (where their signatures are created). I imagine that those symbols are ignored because we know that their signatures can't be trusted to be stable, and we don't expect them to be inspected by a debugger or reflection.

Note that scalac also generates a signature that doesn't correspond to the descriptor in this case (if you force scalac to emit signatures, normally it wouldn't generate one):

class Foo {
  def m[T](x: T): Int => T = (y: Int) => x
}
  public static final T $anonfun$m$1(int);
    descriptor: (Ljava/lang/Object;I)Ljava/lang/Object;
    // code...
    Signature: #76                          // (I)TT;

The discrepancy is not caught by -Xverify either.

@smarter smarter added this to the 0.5 Tech Preview milestone Nov 22, 2017
@Duhemm
Copy link
Contributor Author

Duhemm commented Nov 28, 2017

@smarter I've been looking into the wrong signatures for lifted methods and classes.

I've noticed that lifted methods do not get the Lifted flag (actually, it looks like it's never set anywhere in the compiler). I've added it for lifted DefDefs. This fixes the issue of wrong generic signatures for lifted DefDefs, because we don't generate generic signatures for symbols that have the flags Method and Lifted (assuming I didn't miss other spots, or set the flag too often).

Lifted classes are another story. In this example, scalac gives the class the outer reference during explicitOuter, whereas dotty adds it during lambdaLift:

class Foo {
  def m[T](x: T): Int => T = {
    def bar(y: Int) = x
    class buzz[U](z: Int) { def boo = x.toString + z.toString }
    bar _
  }
}

I'm not sure what to do here.

@@ -444,6 +444,7 @@ object LambdaLift {

def liftDef(tree: MemberDef)(implicit ctx: Context): Tree = {
val buf = liftedDefs(tree.symbol.owner)
tree.symbol.setFlag(Lifted)
Copy link
Member

Choose a reason for hiding this comment

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

@smarter
Copy link
Member

smarter commented Nov 28, 2017

When a class is lifted from a method it should not get a generic signature since there is no reason to call it from outside (in fact, we should probably emit them as private classes). When a class is lifted from another class it should get a generic signature, but this seems to already be working correctly:

class Foo(x: Int) {
  class buzz[U](z: U) { def boo = x.toString + z.toString }
}

@smarter
Copy link
Member

smarter commented Dec 20, 2017

Ping ?

@Duhemm
Copy link
Contributor Author

Duhemm commented Mar 27, 2018

@smarter I've addressed your comment and added a test.

@smarter
Copy link
Member

smarter commented Mar 27, 2018

@smarter I've addressed your comment and added a test.

Which comment exactly? I just tried the example from #3503 (comment) and you're still generating a generic signature for the local class buzz.

@@ -500,7 +500,8 @@ object GenericSignatures {
case PolyType(_, _) =>
true
case ClassInfo(_, _, parents, _, _) =>
foldOver(tp.typeParams.nonEmpty, parents)
// Local classes don't need a signature
!tp.classSymbol.owner.is(Method) && foldOver(tp.typeParams.nonEmpty, parents)
Copy link
Contributor

Choose a reason for hiding this comment

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

SymUtils::isLocal

@Duhemm
Copy link
Contributor Author

Duhemm commented Mar 28, 2018

@smarter Sorry, I meant this one: #3503 (comment).

I've changed the PR so that no signature is generated for local classes.

However, the constructor of local classes may still get a wrong signature. I imagine that this is okay, since scalac will also generate a signature that doesn't match the descriptor:

dotc:

public <U extends java.lang.Object> Foo$buzz$1(int);
  descriptor: (Ljava/lang/Object;I)V
  // ...
  Signature: #48                          // <U:Ljava/lang/Object;>(I)V

scalac:

public Foo$buzz$1(Foo, int);
  descriptor: (LFoo;ILjava/lang/Object;)V
  // ...
  Signature: #53                          // (LFoo;I)V

Finally, not generating signatures for local classes means that code such as:

def foo = {
  class Foo[U, T <: java.util.List[U]]
  classOf[Foo[_, _]].getTypeParameters().mkString(", ")
}

will produce different outputs when compiled with scalac or dotc. I don't think this is really a valid use case, but it may be a surprising behavior.

@smarter
Copy link
Member

smarter commented Mar 28, 2018

However, the constructor of local classes may still get a wrong signature.

To be consistent, the methods inside a local class should not get signatures either.

Copy link
Member

@smarter smarter left a comment

Choose a reason for hiding this comment

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

  • Please add more testcases (class inside method, class inside class inside method, ...)
  • Is it possible to improve -Xverify-signature to catch the kind of invalid signatures we were generating before this PR?

@Duhemm
Copy link
Contributor Author

Duhemm commented Apr 10, 2018

I've changed the PR to avoid generating signatures for all local symbols, and added more test cases.

I'd be happy to improve -Xverify-signatures, which only does syntactical checks at the moment. I've been looking a bit at the backend, and I Imagine that we could parse the generic signatures and compare the number of parameters and their erased types with that of the method descriptor.

What do you think? Could we leave that for another PR?

@allanrenucci allanrenucci assigned smarter and unassigned Duhemm Apr 10, 2018
@smarter
Copy link
Member

smarter commented Apr 10, 2018

What do you think? Could we leave that for another PR?

Sure.

@smarter
Copy link
Member

smarter commented Apr 10, 2018

test performance please

@dottybot
Copy link
Member

performance test scheduled: 1 job(s) in queue, 1 running.

@dottybot
Copy link
Member

Performance test finished successfully:

Visit http://dotty-bench.epfl.ch/3503/ to see the changes.

Benchmarks is based on merging with master (3cba055)

@smarter smarter merged commit 2762567 into scala:master Apr 10, 2018
@Duhemm Duhemm deleted the fix/3476 branch November 30, 2018 07:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants