There has been a change in the Matrix

54 views
Skip to first unread message

√iktor Ҡlang

unread,
Jun 25, 2012, 7:28:33 AM6/25/12
to scala-i...@googlegroups.com
Hey guys,

The following code worked flawlessly on 2.9:


object japi {
@deprecated("Do not use this directly, use subclasses of this", "2.0")
  class UnitFunctionBridge[-T] extends (T ⇒ BoxedUnit) {
    override final def apply(t: T): BoxedUnit = {
      internal(t)
      BoxedUnit.UNIT
    }
    protected def internal(result: T): Unit = ()
  }
}

abstract class Foreach[-T] extends japi.UnitFunctionBridge[T] {
  override final def internal(t: T): Unit = each(t)

  /**
   * This method will be invoked once when/if a Future that this callback is registered on
   * becomes successfully completed
   */
  @throws(classOf[Throwable])
  def each(result: T): Unit
}


And then from Java:

 @Test
  public void mustBeAbleToForeachAFuture() throws Throwable {
    final CountDownLatch latch = new CountDownLatch(1);
    Promise<String> cf = Futures.promise(system.dispatcher());
    Future<String> f = cf;
    f.foreach(new Foreach<String>() {
      public void each(String future) {
        latch.countDown();
      }
    });

    cf.success("foo");
    assertTrue(latch.await(5000, TimeUnit.MILLISECONDS));
    assertEquals(Await.result(f, timeout), "foo");
  }


However, using Scala 2.10-M4 javac spits this in mah face:

[error] /Users/viktorklang/Documents/workspace/akka/akka/akka-actor-tests/src/test/java/akka/dispatch/JavaFutureTests.java:117: apply$mcLJ$sp(long) in akka.dispatch.japi.UnitFunctionBridge cannot implement apply$mcLJ$sp(long) in scala.Function1; attempting to use incompatible return type
[error] found   : java.lang.Object
[error] required: scala.runtime.BoxedUnit
[error]     f.foreach(new Foreach<String>() {
[error]                                     ^
[error] 1 error
[error] {file:/Users/viktorklang/Documents/workspace/akka/akka/}akka-actor-tests/test:compile: javac returned nonzero exit code
[error] Total time: 125 s, completed Jun 25, 2012 1:27:58 PM

Somebody set me up the bomb?

Cheers,

--
Viktor Klang

Akka Tech Lead
Typesafe - The software stack for applications that scale

Twitter: @viktorklang

√iktor Ҡlang

unread,
Jun 25, 2012, 7:46:14 AM6/25/12
to scala-i...@googlegroups.com
This steps around it... But I think it has a certain kind of smell:

  @deprecated("Do not use this directly, use subclasses of this", "2.0")
  class UnitFunctionBridge[-T] extends (T ⇒ BoxedUnit) {
    final def apply$mcLJ$sp(l: Long): BoxedUnit = { internal(l.asInstanceOf[T]); BoxedUnit.UNIT }
    final def apply$mcLI$sp(i: Int): BoxedUnit = { internal(i.asInstanceOf[T]); BoxedUnit.UNIT }
    final def apply$mcLF$sp(f: Float): BoxedUnit = { internal(f.asInstanceOf[T]); BoxedUnit.UNIT }
    final def apply$mcLD$sp(d: Double): BoxedUnit = { internal(d.asInstanceOf[T]); BoxedUnit.UNIT }

Paul Phillips

unread,
Jun 25, 2012, 11:29:03 AM6/25/12
to scala-i...@googlegroups.com

On Mon, Jun 25, 2012 at 4:28 AM, √iktor Ҡlang <viktor...@gmail.com> wrote:
The following code worked flawlessly on 2.9:

I believe this to be a consequence of SI-3452 in combination with specializing Function1 on AnyRef.  Look on, and despair:

  https://issues.scala-lang.org/browse/SI-3452

I observe the following changes in the generated bytecode if I build your example after cherrypicking my almost-fix for SI-3452, which look sufficient based on your stated workaround.


Differerences:

5c5
< public boolean japi$UnitFunctionBridge.apply$mcZL$sp(T)
---
> public boolean japi$UnitFunctionBridge.apply$mcZL$sp(java.lang.Object)
10c10
< public double japi$UnitFunctionBridge.apply$mcDL$sp(T)
---
> public double japi$UnitFunctionBridge.apply$mcDL$sp(java.lang.Object)
17c17
< public float japi$UnitFunctionBridge.apply$mcFL$sp(T)
---
> public float japi$UnitFunctionBridge.apply$mcFL$sp(java.lang.Object)
22c22
< public int japi$UnitFunctionBridge.apply$mcIL$sp(T)
---
> public int japi$UnitFunctionBridge.apply$mcIL$sp(java.lang.Object)
31c31
< public long japi$UnitFunctionBridge.apply$mcJL$sp(T)
---
> public long japi$UnitFunctionBridge.apply$mcJL$sp(java.lang.Object)
36c36
< public void japi$UnitFunctionBridge.apply$mcVL$sp(T)
\ No newline at end of file
---
> public void japi$UnitFunctionBridge.apply$mcVL$sp(java.lang.Object)
\ No newline at end of file

But I am no longer working on issues such as these.

Paul Phillips

unread,
Jun 25, 2012, 11:34:29 AM6/25/12
to scala-i...@googlegroups.com
I'll enclose my comment from that commit so it doesn't get overlooked.


    Almost-fix for SI-3452.
    
    At this commit ant test-opt has two test failures:
    
      test/files/pos/javaReadsSigs [FAILED]
      test/files/run/t4238 [FAILED]
    
    Fix for wrong bytecode in forwarders.
    
    This took me so long to figure out I can't even tell you. Partly because
    there were two different bugs, one which only arose for trait forwarders
    and one for mirror class forwarders, and every time I'd make one set
    of tests work another set would start failing. The runtime failures
    associated with these bugs were fairly well hidden because you usually
    have to go through java to encounter them: scala doesn't pay that much
    attention to generic signatures, so they can be wrong and scala might still
    generate correct code. But java is not so lucky.
    
    Bug #1)
    
    During mixin composition, classes which extend traits receive forwarders
    to the implementations. An attempt was made to give these the correct
    info (in method "cloneBeforeErasure") but it was prone to giving
    the wrong answer, because: the key attribute which the forwarder
    must capture is what the underlying method will erase to *where the
    implementation is*, not how it appears to the class which contains it.
    That means the signature of the forwarder must be no more precise than
    the signature of the inherited implementation unless additional measures
    will be taken.
    
    This subtle difference will put on an unsubtle show for you in test
    run/t3452.scala.
    
      trait C[T]
      trait Search[M] { def search(input: M): C[Int] = null }
      object StringSearch extends Search[String] { }
      StringSearch.search("test");  // java
      // java.lang.NoSuchMethodError: StringSearch.search(Ljava/lang/String;)LC;
    
    Before/after this commit:
    
      <   signature                                search  (Ljava/lang/String;)LC<Ljava/lang/Object;>;
      ---
      >   signature                                search  (Ljava/lang/Object;)LC<Ljava/lang/Object;>;
    
    Bug #2) The same principle is at work, at a different location.
    During genjvm, objects without declared companion classes
    are given static forwarders in the corresponding class, e.g.
    
      object Foo { def bar = 5 }
    
    which creates these classes (taking minor liberties):
    
      class Foo$ { static val MODULE$ = new Foo$ ; def bar = 5 }
      class Foo  { static def bar = Foo$.MODULE$.bar }
    
    In generating these, genjvm circumvented the usual process whereby one
    creates a symbol and gives it an info, preferring to target the bytecode
    directly. However generic signatures are calculated from symbol info
    (in this case reusing the info from the module class.) Lacking even the
    attempt which was being made in mixin to "clone before erasure", we
    would have runtime failures of this kind:
    
      abstract class Foo {
        type T
        def f(x: T): List[T] = List()
      }
      object Bar extends Foo { type T = String }
      Bar.f("");    // java
      // java.lang.NoSuchMethodError: Bar.f(Ljava/lang/String;)Lscala/collection/immutable/List;
    
    Before/after this commit:
    
    <   signature                                     f  (Ljava/lang/String;)Lscala/collection/immutable/List<Ljava/lang/String;>;
    ---
    >   signature                                     f  (Ljava/lang/Object;)Lscala/collection/immutable/List<Ljava/lang/Object;>;
    
    Closes SI-3452.

martin odersky

unread,
Jun 25, 2012, 11:45:05 AM6/25/12
to scala-i...@googlegroups.com
I think a ticket is in order.

 - Martin
--
Martin Odersky
Prof., EPFL and Chairman, Typesafe
PSED, 1015 Lausanne, Switzerland
Tel. EPFL: +41 21 693 6863
Tel. Typesafe: +41 21 691 4967

√iktor Ҡlang

unread,
Jun 25, 2012, 12:05:00 PM6/25/12
to scala-i...@googlegroups.com

√iktor Ҡlang

unread,
Jun 25, 2012, 12:22:44 PM6/25/12
to scala-i...@googlegroups.com


On Mon, Jun 25, 2012 at 6:05 PM, √iktor Ҡlang <viktor...@gmail.com> wrote:
Ticket ordered: https://issues.scala-lang.org/browse/SI-5976


Thanks guys! Hope you can nail that one! :-)
Reply all
Reply to author
Forward
0 new messages