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

Unify treatment of built-in functions and SAMs #4971

Merged
merged 36 commits into from Mar 31, 2016

Conversation

Projects
None yet
9 participants
@adriaanm
Member

adriaanm commented Feb 17, 2016

Function literal can have user-defined SAM or built-in function type.

A function literal, such as x => x is now accepted when a
Single Abstract Method (SAM) type is expected, in addition to
the usual Scala function type (e.g., Int => Int). A SAM type
is Java 8's convention for functions, so that e.g., Runnable
is now recognized as a function type.

Traditionally, Scala has shipped with FunctionN classes to
represent functions, with Int => Int being syntactic sugar
for Function1[Int, Int]. Scala 2.11 introduced experimental
support for expanding function literals to anonymous subclasses
of SAM type, and with Scala 2.12.0-M4, we now compile these
literals to the same bytecode as Java 8, whether targeting a
FunctionN or a user-defined functional interface.

Thanks to our new trait encoding where a trait compiles to
a Java 8 interface, our built-in FunctionN traits now also
compile to Functional Interfaces, so that Java programmers
can use Java 8's syntax for closures to target them.

If your project defines an implicit conversion from a SAM type
to a compatible Scala FunctionN type, this conversion will
no longer trigger, as the compiler performs SAM conversion
before looking for an implicit view, which enables generating
more efficient bytecode (using invokedynamic & LambdaMetaFactory).
To retain the old behavior, you may compile under -Xsource:2.11,
or disqualify the type from being a SAM (e.g., by adding a
second abstract method).

For example,

trait MySam { def apply(x: Int): String }
implicit def fun2sam(fun: Int => String): MySam = new MySam { def apply(x: Int) = fun(x) } // not used! 
val sammy: MySam = (x: Int) => x.toString

Here, SAM conversion from the function literal (x: Int) => x.toString
to the expected SAM type MySam takes precedence over inserting
the implicit view fun2sam((x: Int) => x.toString), which is how the
2.11 type checker expands the last line of the example.

Examples: Source Incompatible Change

Here are some snippets that compile in 2.11, but fail in 2.12. Roughly speaking, this is because SAM types and FunctionN types now compete harder during overload resolution. The Function type still wins (as explained in the spec update), but type inference breaks down when we cannot reduce the overloaded candidates down to one before type checking the arguments:

In the 1MLoC of the community build, we had less than a handful of lines that needed to change. Here's an example from akka-actor:

abstract class ActorRef extends java.lang.Comparable[ActorRef]

class Index[V](val valueComparator: java.util.Comparator[V]) {
  def this(cmp: (V, V) ⇒ Int) = this(new java.util.Comparator[V] {
    def compare(a: V, b: V): Int = cmp(a, b)
  })
}

object MessageDispatcher {
  // does not type check: call overloaded ctor...
  // new Index[ActorRef](_ compareTo _)
  // either pick an alternative explicitly
  new Index[ActorRef]((_ compareTo _): ju.Comparator[ActorRef]))
  // or provide explicit argument types:
  new Index[ActorRef]((_: ActorRef) compareTo (_: ActorRef))
}

Implementation Details and Restrictions

Scala considers any top-level class (trait or not) with exactly
one abstract method and an accessible zero-argument a SAM type.
(See the language specification for more details.)

On the Java 8 platform, only a subset of our SAM types can be
compiled to a combination of invokedynamic and LambdaMetaFactory,
to avoid having to emit bytecode for the anymous class at
compile time. Since Java 8's LambdaMetaFactory (LMF) is not
aware of Scala's trait encoding, it can only properly
instantiate traits that correspond to "pure" Java interfaces --
that is, interfaces that do not require synthetic members to
be implemented (by the compiler) when inherited in a proper class.

This rules out nested traits, traits with fields, traits that
extend a non-trait class (except for Object), traits with any
statements in their template body (for example, LMF would
not be able to run the initializer that's required to
execute the println in trait T { println("hello!") }),

Traits that cannot be instantiated by LMF are still SAM targets,
the compiler simply creates anonymous subclasses as before,
instead of deferring spinning them up at linkage time
(when invokedynamic's bootstrap method calls LMF).

Finally, when a SAM type is specialized (more precisely,
its specialized type parameter is instantiated with a
type for which it is specialized), we do not use
LambdaMetaFactory as incompatible with Scala specialization.

Specialization

The current specialization scheme is not amenable to using
LambdaMetaFactory to spin up subclasses. Since the generic
method is abstract, and the specialized ones are concrete,
specialization is rendered moot because we cannot implement
the specialized method with the lambda using LMF.

For the specialized versions of built-in function types,
we use hand-crafted versions, as illustrated next.
Notice how apply$mcB$sp is looking pretty SAMmy:

@FunctionalInterface
public interface JFunction0$mcB$sp extends JFunction0 {
    @Override
    public byte apply$mcB$sp();

    @Override
    default public Object apply() {
        return BoxesRunTime.boxToByte(this.apply$mcB$sp());
    }
}

Contrast this with our specialized standard FunctionN:

public interface Function0<R> {
    public R apply();

    default public byte apply$mcB$sp() {
        return BoxesRunTime.unboxToByte(this.apply());
    }
}

public interface Function0$mcB$sp extends Function0<Object> { }

The single abstract method in Function0$mcB$sp is apply, and
the method that would let us avoid boxing, if it were abstract,
is apply$mcB$sp...

Type Checker Internals

A function node is first type checked, and parameter types are
inferred, regardless of whether the expected function type is one
of our built-in FunctionN classes, or a user-defined Single
Abstract Method type (argument types are derived from both kinds
of expected types).

typedFunction always assigns a built-in FunctionN type to the
tree, though.

Next, if the expected type is a (polymorphic) SAM type, this
creates a tension between the tree's type and the expect type.
This gap is closed by the adapt method using inferSamType,
which attaches a SAMFunction attachment to the Function node,
so that the back-end can target the right method & type when
emitting the invokedynamic instruction.

We may want to improve this implementation to avoid re-typechecking
function nodes to expected SAM types, as this is pretty tricky
to juggle between erasure and specialization retypechecking trees.

Tree Shape of an expanded Function literal

As with the delambdafy phase, a function literal's body is lifted
out to a method in the surrounding scope (the "target method"),
with the actual function instance created from a lightweight
subclass of the expected type (or the corresponding
invokedynamic + LMF combo).

TODO 2.0

  • confirm scala-js is happy: 8e32d00 should have all the needed fixes
  • Using eta-expansion on a by-name parameter where the expected type is a SAM with 0 argument produces invalid code: the Function0 is cast to the SAM type, rather than adapting it.

TODO M5

  • javac only adds serialization support to LMF-based lambdas when the target type already inherits Serializable. Not sure which way we should go here.
  • should we print SAM functions differently from regular functions? One idea would be to print them as a Typed(fun, typeAtCurrentPhase(fun.attachments.head.samTpe)). Right now, we need to use -Xprint-types to see the difference.
  • We should give guidance to folks who are pattern matching on Function nodes in macros or compiler plugins (perhaps via quasiquotes) that they will need to discriminate based on the tree type if they only want to work with bona fide functions.
  • specialization???
  • remove scala/runtime/java8/JFunction* (need new STARR?)

@scala-jenkins scala-jenkins added this to the 2.12.0-M4 milestone Feb 17, 2016

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Feb 18, 2016

Member

TODO list

@bvenners & @sjrd ask:

  • interaction with implicit search

@retronym suggests:

  • serialization
  • can a SAM become an un-SAM after typers? (--> Nested/local types) add tests
  • also, exclude specialized types
  • (deferred) interaction with miniboxing

from @lrytz:

  • setting to disable sam conversion (-Xsource:2.11)

  • tests: by name

  • junit test for bytecode emitted for various VC/Any/Unit combos

  • check overloading spec, add test for ToString example to test/files/pos/sammy_overload.scala

  • update comments in test/files/pos/t9449.scala

  • more tests based on https://gist.github.com/lrytz/ec4da087dc65e421eb7d

  • remove mention of <static> from spec

  • remove comment from + functionType(vparams map (_ => AnyTpe), shapeType(body)) // TODO: should this be erased when retyping during erasure?, replace with lrytz's clarification:

    We don't even get here during erasure - overloading resolution happens during type checking. During erasure, the condition above (fun.symbol.isOverloaded) is false.

  • observed failure in overloading?

scala> trait ToString { def convert(x: Int): String }
defined trait ToString

scala> object O {
     |   def m(x: Int => String): Int = 0
     |   def m(x: ToString): Int = 1
     | }
defined object O

scala> O.m(_.toString)
<console>:14: error: missing parameter type for expanded function ((x$1: <error>) => x$1.toString)
       O.m(_.toString)
           ^
  • figure out boxing (unit <-> Void etc)
Caused by: java.lang.invoke.LambdaConversionException: 
Type mismatch for lambda expected return: class java.lang.Object is not convertible to void

  • Test based on question by @bvenners regarding implicit view / sam conversion:
trait MySam { def apply(x: Int): String }

def statusQuo = {
  var ok = false
  implicit def fun2sam(fun: Int => String): MySam = { ok = true; new MySam { def apply(x: Int) = fun(x) } }
  val className = (((x: Int) => x.toString): MySam).getClass.toString
  assert(ok, "implicit conversion not called")
  assertContains(className, "$anon$")
  assertNotContains(className, "$$Lambda$")
}

def statusIndy = {
  val className = (((x: Int) => x.toString): MySam).getClass.toString
  assertContains(className, "$$Lambda$")
  assertNotContains(className, "$anon$")
}
Member

adriaanm commented Feb 18, 2016

TODO list

@bvenners & @sjrd ask:

  • interaction with implicit search

@retronym suggests:

  • serialization
  • can a SAM become an un-SAM after typers? (--> Nested/local types) add tests
  • also, exclude specialized types
  • (deferred) interaction with miniboxing

from @lrytz:

  • setting to disable sam conversion (-Xsource:2.11)

  • tests: by name

  • junit test for bytecode emitted for various VC/Any/Unit combos

  • check overloading spec, add test for ToString example to test/files/pos/sammy_overload.scala

  • update comments in test/files/pos/t9449.scala

  • more tests based on https://gist.github.com/lrytz/ec4da087dc65e421eb7d

  • remove mention of <static> from spec

  • remove comment from + functionType(vparams map (_ => AnyTpe), shapeType(body)) // TODO: should this be erased when retyping during erasure?, replace with lrytz's clarification:

    We don't even get here during erasure - overloading resolution happens during type checking. During erasure, the condition above (fun.symbol.isOverloaded) is false.

  • observed failure in overloading?

scala> trait ToString { def convert(x: Int): String }
defined trait ToString

scala> object O {
     |   def m(x: Int => String): Int = 0
     |   def m(x: ToString): Int = 1
     | }
defined object O

scala> O.m(_.toString)
<console>:14: error: missing parameter type for expanded function ((x$1: <error>) => x$1.toString)
       O.m(_.toString)
           ^
  • figure out boxing (unit <-> Void etc)
Caused by: java.lang.invoke.LambdaConversionException: 
Type mismatch for lambda expected return: class java.lang.Object is not convertible to void

  • Test based on question by @bvenners regarding implicit view / sam conversion:
trait MySam { def apply(x: Int): String }

def statusQuo = {
  var ok = false
  implicit def fun2sam(fun: Int => String): MySam = { ok = true; new MySam { def apply(x: Int) = fun(x) } }
  val className = (((x: Int) => x.toString): MySam).getClass.toString
  assert(ok, "implicit conversion not called")
  assertContains(className, "$anon$")
  assertNotContains(className, "$$Lambda$")
}

def statusIndy = {
  val className = (((x: Int) => x.toString): MySam).getClass.toString
  assertContains(className, "$$Lambda$")
  assertNotContains(className, "$anon$")
}
@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Feb 18, 2016

Member

@retronym, could you elaborate on 6ad9b44 and how I should extend its logic to indy sammy? My guess would be that we don't need to adapt unless we're supplying a sam to java? EDIT: that's probably wrong, I'll unmelt brain tomorrow and take another stab. Hopefully this will be the last issue, then I'll work on adding test cases.

Member

adriaanm commented Feb 18, 2016

@retronym, could you elaborate on 6ad9b44 and how I should extend its logic to indy sammy? My guess would be that we don't need to adapt unless we're supplying a sam to java? EDIT: that's probably wrong, I'll unmelt brain tomorrow and take another stab. Hopefully this will be the last issue, then I'll work on adding test cases.

@retronym

This comment has been minimized.

Show comment
Hide comment
@retronym

retronym Feb 18, 2016

Member

@adriaanm If the anon-class version of the SAM function would have required a bridge method, we need to either rely on the bridge created by LambdaMetafactory, or we have to create an anonFun$adapted method to perform adaptations pass it to LambdaMetafactory.

The logic in that commit would need to be extended to compare the impl method param types with the corresponding param type of the interface method. Currently, the knowledge the FunctionN parameter and return types erase to ObjectTpe is hard coded in boxedType.

If any of these require:

  • unboxing of a boxed primitive
  • unboxing of a value class

... we need to emit the adapter method.

We also need an adapter if the return type needs to be adapted from void to BoxedUnit (e.g., to conform the the a interface method returning an reference type.)

Member

retronym commented Feb 18, 2016

@adriaanm If the anon-class version of the SAM function would have required a bridge method, we need to either rely on the bridge created by LambdaMetafactory, or we have to create an anonFun$adapted method to perform adaptations pass it to LambdaMetafactory.

The logic in that commit would need to be extended to compare the impl method param types with the corresponding param type of the interface method. Currently, the knowledge the FunctionN parameter and return types erase to ObjectTpe is hard coded in boxedType.

If any of these require:

  • unboxing of a boxed primitive
  • unboxing of a value class

... we need to emit the adapter method.

We also need an adapter if the return type needs to be adapted from void to BoxedUnit (e.g., to conform the the a interface method returning an reference type.)

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Feb 18, 2016

Member

Thanks! I think that's it: I'll see about generalizing the decision about boxing of the SAM's parameters and result type.

Member

adriaanm commented Feb 18, 2016

Thanks! I think that's it: I'll see about generalizing the decision about boxing of the SAM's parameters and result type.

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Feb 21, 2016

Member

I have a good feeling about this last commit 🚀

Member

adriaanm commented Feb 21, 2016

I have a good feeling about this last commit 🚀

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Feb 22, 2016

Member

Only when using a bootstrapped compiler:

java.lang.ClassCastException: [Lscala.collection.mutable.ArraySortingTest$CantSortMe;
cannot be cast to scala.collection.IterableLike
    at scala.runtime.Tuple2Zipped$.forall$extension(Tuple2Zipped.scala:88)

Looking into what's different about Tuple2Zipped.

Member

adriaanm commented Feb 22, 2016

Only when using a bootstrapped compiler:

java.lang.ClassCastException: [Lscala.collection.mutable.ArraySortingTest$CantSortMe;
cannot be cast to scala.collection.IterableLike
    at scala.runtime.Tuple2Zipped$.forall$extension(Tuple2Zipped.scala:88)

Looking into what's different about Tuple2Zipped.

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Feb 22, 2016

Member

I've been poking around without much luck. I suspected the logic in determining which result type to use for the bridge, but not sure. Still trying to find a good way to minimize the bytecode diff between bootstrapped (fails) and non-bootstrapped version (ok).

Member

adriaanm commented Feb 22, 2016

I've been poking around without much luck. I suspected the logic in determining which result type to use for the bridge, but not sure. Still trying to find a good way to minimize the bytecode diff between bootstrapped (fails) and non-bootstrapped version (ok).

@adriaanm adriaanm referenced this pull request Feb 25, 2016

Closed

Turn on sam conversion by default [ci: last-only] #4945

0 of 5 tasks complete

@adriaanm adriaanm changed the title from WIP: INDY sammy [ci: last-only] to WIP: unify treatment of built-in functions and SAMs [ci: last-only] Feb 25, 2016

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Mar 17, 2016

Member

@retronym, good catch on excluding nested/local types from SAM conversion. I haven't found an example of why @specialized types are problematic, though. Did you have one in mind? Also, not sure anymore what the issue with serialization was. Sorry!

Member

adriaanm commented Mar 17, 2016

@retronym, good catch on excluding nested/local types from SAM conversion. I haven't found an example of why @specialized types are problematic, though. Did you have one in mind? Also, not sure anymore what the issue with serialization was. Sorry!

@retronym

This comment has been minimized.

Show comment
Hide comment
@retronym

retronym Mar 17, 2016

Member

I haven't found an example of why @specialized types are problematic, though. Did you have one in mind?

I think I had in mind a correctness problem based on my attempts to use install default methods in scala.FunctionN and use them directly in our indylamba (rather than JFunctionN) , but I can't create an example for that yet.

However, there is a performance problem:

scala> :paste -raw
// Entering paste mode (ctrl-D to finish)

package test

trait F0[@specialized +R] extends AnyRef { self =>
  def apply(): R
}
// Exiting paste mode, now interpreting.

scala> :paste -raw
// Entering paste mode (ctrl-D to finish)

package test { class C { def x: test.F0[Int] = () => 42 } }

// Exiting paste mode, now interpreting.

scala> :javap -v test.C
  public test.F0<java.lang.Object> x();
    Code:
       0: invokedynamic #36,  0             // InvokeDynamic #0:apply:()Ltest/F0;
       5: areturn

scala> :javap -c test.C
...
  public static final int test$C$$$anonfun$1();
    descriptor: ()I
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
    Code:
      stack=1, locals=0, args_size=0
         0: bipush        42
         2: ireturn
      LineNumberTable:
        line 1: 0

  public test.C();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #44                 // Method java/lang/Object."<init>":()V
         4: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Ltest/C;
      LineNumberTable:
        line 1: 0

  public static final java.lang.Object test$C$$$anonfun$1$adapted();
    descriptor: ()Ljava/lang/Object;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
    Code:
      stack=1, locals=0, args_size=0
         0: invokestatic  #46                 // Method test$C$$$anonfun$1:()I
         3: invokestatic  #52                 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
         6: areturn
      LineNumberTable:
        line 1: 0
}
BootstrapMethods:
  0: #22 invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #24 ()Ljava/lang/Object;
      #28 invokestatic test/C.test$C$$$anonfun$1$adapted:()Ljava/lang/Object;
      #24 ()Ljava/lang/Object;
      #29 3
      #30 1
      #32 scala/Serializable
      #33 0
  1: #61 invokestatic scala/runtime/LambdaDeserialize.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;

Or, by dumping a stack trace within the lambda body (including Hidden Frames, #TIL), we see that we route through the boxing bridge test.F0.apply$mcI$sp

% sbt -J-XX:+UnlockDiagnosticVMOptions -J-XX:+ShowHiddenFrames consoleQuick

scala> :paste -raw
// Entering paste mode (ctrl-D to finish)

package test

trait F0[@specialized +R] extends AnyRef { self =>
  def apply(): R
}

// Exiting paste mode, now interpreting.


scala> val x: test.F0[Int] = () => { new Throwable().printStackTrace; 42 }
x: test.F0[Int] = $$Lambda$1872/725725269@3016ce06

scala> x()
java.lang.Throwable
    at $line3.$read$$iw$$iw$.$line3$$read$$iw$$iw$$$anonfun$1(<console>:11)
    at $line3.$read$$iw$$iw$.$line3$$read$$iw$$iw$$$anonfun$1$adapted(<console>:11)
    at $line3.$read$$iw$$iw$$$Lambda$1872/725725269.apply(<Unknown>:1000000)
    at test.F0.apply$mcI$sp(<pastie>:4)
    at $line4.$read$$iw$$iw$.<init>(<console>:13)
Member

retronym commented Mar 17, 2016

I haven't found an example of why @specialized types are problematic, though. Did you have one in mind?

I think I had in mind a correctness problem based on my attempts to use install default methods in scala.FunctionN and use them directly in our indylamba (rather than JFunctionN) , but I can't create an example for that yet.

However, there is a performance problem:

scala> :paste -raw
// Entering paste mode (ctrl-D to finish)

package test

trait F0[@specialized +R] extends AnyRef { self =>
  def apply(): R
}
// Exiting paste mode, now interpreting.

scala> :paste -raw
// Entering paste mode (ctrl-D to finish)

package test { class C { def x: test.F0[Int] = () => 42 } }

// Exiting paste mode, now interpreting.

scala> :javap -v test.C
  public test.F0<java.lang.Object> x();
    Code:
       0: invokedynamic #36,  0             // InvokeDynamic #0:apply:()Ltest/F0;
       5: areturn

scala> :javap -c test.C
...
  public static final int test$C$$$anonfun$1();
    descriptor: ()I
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
    Code:
      stack=1, locals=0, args_size=0
         0: bipush        42
         2: ireturn
      LineNumberTable:
        line 1: 0

  public test.C();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #44                 // Method java/lang/Object."<init>":()V
         4: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Ltest/C;
      LineNumberTable:
        line 1: 0

  public static final java.lang.Object test$C$$$anonfun$1$adapted();
    descriptor: ()Ljava/lang/Object;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
    Code:
      stack=1, locals=0, args_size=0
         0: invokestatic  #46                 // Method test$C$$$anonfun$1:()I
         3: invokestatic  #52                 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
         6: areturn
      LineNumberTable:
        line 1: 0
}
BootstrapMethods:
  0: #22 invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #24 ()Ljava/lang/Object;
      #28 invokestatic test/C.test$C$$$anonfun$1$adapted:()Ljava/lang/Object;
      #24 ()Ljava/lang/Object;
      #29 3
      #30 1
      #32 scala/Serializable
      #33 0
  1: #61 invokestatic scala/runtime/LambdaDeserialize.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;

Or, by dumping a stack trace within the lambda body (including Hidden Frames, #TIL), we see that we route through the boxing bridge test.F0.apply$mcI$sp

% sbt -J-XX:+UnlockDiagnosticVMOptions -J-XX:+ShowHiddenFrames consoleQuick

scala> :paste -raw
// Entering paste mode (ctrl-D to finish)

package test

trait F0[@specialized +R] extends AnyRef { self =>
  def apply(): R
}

// Exiting paste mode, now interpreting.


scala> val x: test.F0[Int] = () => { new Throwable().printStackTrace; 42 }
x: test.F0[Int] = $$Lambda$1872/725725269@3016ce06

scala> x()
java.lang.Throwable
    at $line3.$read$$iw$$iw$.$line3$$read$$iw$$iw$$$anonfun$1(<console>:11)
    at $line3.$read$$iw$$iw$.$line3$$read$$iw$$iw$$$anonfun$1$adapted(<console>:11)
    at $line3.$read$$iw$$iw$$$Lambda$1872/725725269.apply(<Unknown>:1000000)
    at test.F0.apply$mcI$sp(<pastie>:4)
    at $line4.$read$$iw$$iw$.<init>(<console>:13)
@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Mar 17, 2016

Member

Ok, it seems like enough of a corner case to start by excluding them. Can always open it up again.

Member

adriaanm commented Mar 17, 2016

Ok, it seems like enough of a corner case to start by excluding them. Can always open it up again.

@retronym

This comment has been minimized.

Show comment
Hide comment
@retronym

retronym Mar 17, 2016

Member

Serialization seems to work (although warrants a test)

scala> :paste -raw
// Entering paste mode (ctrl-D to finish)

package p2; class C { def test: Runnable = () => println("yo!") }

// Exiting paste mode, now interpreting.

scala> new p2.C().test
res1: Runnable = p2.C$$Lambda$1962/114058013@7f929677

scala>   def serializeDeserialize[T <: AnyRef](obj: T) = {
     |     import java.io._
     |     val buffer = new ByteArrayOutputStream
     |     val out = new ObjectOutputStream(buffer)
     |     out.writeObject(obj)
     |     val in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray))
     |     in.readObject.asInstanceOf[T]
     |   }
serializeDeserialize: [T <: AnyRef](obj: T)T

scala> serializeDeserialize(res1)
res2: Runnable = p2.C$$Lambda$2142/89459519@26ce1bdc

scala> :javap -c -private p2.C
Compiled from "<pastie>"
public class p2.C {
  public java.lang.Runnable test();
    Code:
       0: invokedynamic #36,  0             // InvokeDynamic #0:run:()Ljava/lang/Runnable;
       5: areturn

  public static final void p2$C$$$anonfun$1();
    Code:
       0: getstatic     #44                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
       3: ldc           #46                 // String yo!
       5: invokevirtual #50                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
       8: return

  public p2.C();
    Code:
       0: aload_0
       1: invokespecial #53                 // Method java/lang/Object."<init>":()V
       4: return

  private static java.lang.Object $deserializeLambda$(java.lang.invoke.SerializedLambda);
    Code:
       0: aload_0
       1: invokedynamic #65,  0             // InvokeDynamic #1:lambdaDeserialize:(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;
       6: areturn
}

I wasn't sure whether the backend would add the $deserializeLambda$ method to classes that only contain SAM lambdas (but it does), and that LambdaDeserializer would handle SAM lambdas (it also does, no I remember that I have tests for this already in LambdaDeserializerTest.java.

Member

retronym commented Mar 17, 2016

Serialization seems to work (although warrants a test)

scala> :paste -raw
// Entering paste mode (ctrl-D to finish)

package p2; class C { def test: Runnable = () => println("yo!") }

// Exiting paste mode, now interpreting.

scala> new p2.C().test
res1: Runnable = p2.C$$Lambda$1962/114058013@7f929677

scala>   def serializeDeserialize[T <: AnyRef](obj: T) = {
     |     import java.io._
     |     val buffer = new ByteArrayOutputStream
     |     val out = new ObjectOutputStream(buffer)
     |     out.writeObject(obj)
     |     val in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray))
     |     in.readObject.asInstanceOf[T]
     |   }
serializeDeserialize: [T <: AnyRef](obj: T)T

scala> serializeDeserialize(res1)
res2: Runnable = p2.C$$Lambda$2142/89459519@26ce1bdc

scala> :javap -c -private p2.C
Compiled from "<pastie>"
public class p2.C {
  public java.lang.Runnable test();
    Code:
       0: invokedynamic #36,  0             // InvokeDynamic #0:run:()Ljava/lang/Runnable;
       5: areturn

  public static final void p2$C$$$anonfun$1();
    Code:
       0: getstatic     #44                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
       3: ldc           #46                 // String yo!
       5: invokevirtual #50                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
       8: return

  public p2.C();
    Code:
       0: aload_0
       1: invokespecial #53                 // Method java/lang/Object."<init>":()V
       4: return

  private static java.lang.Object $deserializeLambda$(java.lang.invoke.SerializedLambda);
    Code:
       0: aload_0
       1: invokedynamic #65,  0             // InvokeDynamic #1:lambdaDeserialize:(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;
       6: areturn
}

I wasn't sure whether the backend would add the $deserializeLambda$ method to classes that only contain SAM lambdas (but it does), and that LambdaDeserializer would handle SAM lambdas (it also does, no I remember that I have tests for this already in LambdaDeserializerTest.java.

@retronym

This comment has been minimized.

Show comment
Hide comment
@retronym

retronym Mar 17, 2016

Member

I wonder if we can/should find a way to make the same exclusion for @miniboxed types. Obviously this one can come in a followup change after consultation with @VladUreche

Member

retronym commented Mar 17, 2016

I wonder if we can/should find a way to make the same exclusion for @miniboxed types. Obviously this one can come in a followup change after consultation with @VladUreche

@sjrd

This comment has been minimized.

Show comment
Hide comment
@sjrd

sjrd Mar 18, 2016

Member

@adriaanm Er ... I would very much hope that the SAM treatment gets precedence over implicit conversion! What if there is no explicit type on the lambda parameter? The implicit would not be applicable, but the SAM treatment should. And that means that, if implicit takes precedence over SAM, the behavior would be different whether or not there's an explicit type on the parameter.

Member

sjrd commented Mar 18, 2016

@adriaanm Er ... I would very much hope that the SAM treatment gets precedence over implicit conversion! What if there is no explicit type on the lambda parameter? The implicit would not be applicable, but the SAM treatment should. And that means that, if implicit takes precedence over SAM, the behavior would be different whether or not there's an explicit type on the parameter.

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Mar 18, 2016

Member

If the implicit didn't apply before, it still won't, and thus SAM will. I don't think we can preempt an implicit conversion that applied in 2.11 with a sam conversion in 2.12, as we have to maintain backwards compatibility. It's hard to imagine what the difference would be, but it's the conservative thing to do, no?

In other words, I think your example is fine under my proposal, unless I'm misunderstanding?

Member

adriaanm commented Mar 18, 2016

If the implicit didn't apply before, it still won't, and thus SAM will. I don't think we can preempt an implicit conversion that applied in 2.11 with a sam conversion in 2.12, as we have to maintain backwards compatibility. It's hard to imagine what the difference would be, but it's the conservative thing to do, no?

In other words, I think your example is fine under my proposal, unless I'm misunderstanding?

@sjrd

This comment has been minimized.

Show comment
Hide comment
@sjrd

sjrd Mar 18, 2016

Member

I'm worried about the surprise factor: adding or removing the type on the parameter can change the behavior of the program.

Backwards compatibility is one thing, but making the language more surprising and less consistent in its name seems dangerous to me, especially since there's no hope to fix that later. :-s

Member

sjrd commented Mar 18, 2016

I'm worried about the surprise factor: adding or removing the type on the parameter can change the behavior of the program.

Backwards compatibility is one thing, but making the language more surprising and less consistent in its name seems dangerous to me, especially since there's no hope to fix that later. :-s

@soc

This comment has been minimized.

Show comment
Hide comment
@soc

soc Mar 18, 2016

Member

@sjrd I agree. I think people will be more willing to accept slight changes with the introduction of Java 8 in 2.12, than if it would happen in some other, random release later. Can't we have a community build and see how much stuff breaks? If there is too much breakage, we could add a (deprecated) compiler flag that picks SAMs last til people can adapt their code.

Member

soc commented Mar 18, 2016

@sjrd I agree. I think people will be more willing to accept slight changes with the introduction of Java 8 in 2.12, than if it would happen in some other, random release later. Can't we have a community build and see how much stuff breaks? If there is too much breakage, we could add a (deprecated) compiler flag that picks SAMs last til people can adapt their code.

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Mar 18, 2016

Member

We did run a community build already. The main breakage is due to change in overloading behavior. A Sam type and a function type now compete whereas before one candidate would usually be picked and type inference would work.

I still don't see the issue you're referring to, @sjrd, could you give an example? Note that functions are type checked first, regardless of whether our built in function type or a Sam type is expected. The same inference is performed for argument types. Then, we compare to the expected type again to the fully defined function type and close the gap by implicit view or Sam conversion in turn.

Member

adriaanm commented Mar 18, 2016

We did run a community build already. The main breakage is due to change in overloading behavior. A Sam type and a function type now compete whereas before one candidate would usually be picked and type inference would work.

I still don't see the issue you're referring to, @sjrd, could you give an example? Note that functions are type checked first, regardless of whether our built in function type or a Sam type is expected. The same inference is performed for argument types. Then, we compare to the expected type again to the fully defined function type and close the gap by implicit view or Sam conversion in turn.

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Mar 18, 2016

Member

Well, I started probing around the interaction with implicits. First stop: some sanity for inferImplicit and its thousand boolean levers.

Member

adriaanm commented Mar 18, 2016

Well, I started probing around the interaction with implicits. First stop: some sanity for inferImplicit and its thousand boolean levers.

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Mar 18, 2016

Member

To make things more concrete, is this what you have in mind?

scala> trait F[A, B] { def apply(x: A): B } 
defined trait F

scala> (x => x): F[Int, Int]
res0: F[Int,Int] = $$Lambda$2037/439232821@733c423e

scala> (x => x): F[_, _]
res1: F[_, _] = $$Lambda$2038/275754769@70f43b45

scala> implicit def fun2F[T, U](f: T => U): F[T, U] = new F[T, U] { def apply(x: T) = f(x) }
warning: there was one feature warning; re-run with -feature for details
fun2F: [T, U](f: T => U)F[T,U]

scala> (x => x): F[Int, Int] // implicit kicks in
res2: F[Int,Int] = $anon$1@7f4037ed

scala> (x => x): F[_, _] // implicit doesn't work (this doesn't compile in 2.11.x), but SAM conversion makes it okay in 2.12
res3: F[_, _] = $$Lambda$2040/397309480@1136b469

I think this prioritization is desirable, since you have control over which implicits are in scope, but not over SAM adaptation. If we did SAM first, you could never get an implicit conversion to kick in anymore between a function type and a SAM type. /cc @odersky

Member

adriaanm commented Mar 18, 2016

To make things more concrete, is this what you have in mind?

scala> trait F[A, B] { def apply(x: A): B } 
defined trait F

scala> (x => x): F[Int, Int]
res0: F[Int,Int] = $$Lambda$2037/439232821@733c423e

scala> (x => x): F[_, _]
res1: F[_, _] = $$Lambda$2038/275754769@70f43b45

scala> implicit def fun2F[T, U](f: T => U): F[T, U] = new F[T, U] { def apply(x: T) = f(x) }
warning: there was one feature warning; re-run with -feature for details
fun2F: [T, U](f: T => U)F[T,U]

scala> (x => x): F[Int, Int] // implicit kicks in
res2: F[Int,Int] = $anon$1@7f4037ed

scala> (x => x): F[_, _] // implicit doesn't work (this doesn't compile in 2.11.x), but SAM conversion makes it okay in 2.12
res3: F[_, _] = $$Lambda$2040/397309480@1136b469

I think this prioritization is desirable, since you have control over which implicits are in scope, but not over SAM adaptation. If we did SAM first, you could never get an implicit conversion to kick in anymore between a function type and a SAM type. /cc @odersky

@VladUreche

This comment has been minimized.

Show comment
Hide comment
@VladUreche

VladUreche Mar 19, 2016

Member

I wonder if we can/should find a way to make the same exclusion for @miniboxed types. Obviously this one can come in a followup change after consultation with @VladUreche

Thanks for the ping @retronym! If I understood your previous example correctly, SAMs make function specialization redundant, since the method boxes and the adapter unboxes. Is this accurate?

What I envision is an approach where the compiler would check the SAM for specialization and, if it is specialized, it would adapt the method (anonfun$1$specialized) and update the SAM used for invokeDynamic. Do you have an idea how difficult it would be to do this in the current infrastructure?

Also ping @DarkDimius, who may have faced this problem on the dotty side.

Member

VladUreche commented Mar 19, 2016

I wonder if we can/should find a way to make the same exclusion for @miniboxed types. Obviously this one can come in a followup change after consultation with @VladUreche

Thanks for the ping @retronym! If I understood your previous example correctly, SAMs make function specialization redundant, since the method boxes and the adapter unboxes. Is this accurate?

What I envision is an approach where the compiler would check the SAM for specialization and, if it is specialized, it would adapt the method (anonfun$1$specialized) and update the SAM used for invokeDynamic. Do you have an idea how difficult it would be to do this in the current infrastructure?

Also ping @DarkDimius, who may have faced this problem on the dotty side.

@VladUreche

This comment has been minimized.

Show comment
Hide comment
@VladUreche

VladUreche Mar 19, 2016

Member

@adriaanm, for what it's worth, I think your approach to first trigger implicits and then indy adds the least surprise: if there are indy-like implicits in scope (e.g. (x=>x) => IntToIntSAM), they will continue being triggered. For the other cases, when either the implicit or the indy applies, there is no confusion.

Member

VladUreche commented Mar 19, 2016

@adriaanm, for what it's worth, I think your approach to first trigger implicits and then indy adds the least surprise: if there are indy-like implicits in scope (e.g. (x=>x) => IntToIntSAM), they will continue being triggered. For the other cases, when either the implicit or the indy applies, there is no confusion.

@soc

This comment has been minimized.

Show comment
Hide comment
@soc

soc Mar 19, 2016

Member

I would prefer if SAMs would be treated as just an ordinary function type with no distinction between FunctionX types and other types, instead of thinking in terms of "converting stuff to SAMs". A user shouldn't have to think about it. If a user writes an anonymous function, the SAM should be a valid signature.

Letting implicits interfere with using SAMs feels lot like letting implicits interfere with other basic parts like eta-expansion or letting users overload the ..

Member

soc commented Mar 19, 2016

I would prefer if SAMs would be treated as just an ordinary function type with no distinction between FunctionX types and other types, instead of thinking in terms of "converting stuff to SAMs". A user shouldn't have to think about it. If a user writes an anonymous function, the SAM should be a valid signature.

Letting implicits interfere with using SAMs feels lot like letting implicits interfere with other basic parts like eta-expansion or letting users overload the ..

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Mar 19, 2016

Member

@soc, from the spec:


Translation

If the expected type of the anonymous function is of the shape scala.Function$n$[$S_1 , \ldots , S_n$, $R\,$], or can be SAM-converted to such a function type, the type $T_i$ of a parameter $x_i$ can be omitted, as far as $S_i$ is defined in the expected type, and $T_i$ = $S_i$ is assumed. Furthermore, the expected type when type checking $e$ is $R$.

[snip]

SAM conversion

An expression (p1, ..., pN) => body of function type (T1, ..., TN) => T is sam-convertible to the expected type S if the following holds:


I think this is pretty close to what you're envisioning. The only difference is that there could be an implicit that applies instead of SAM conversion, but this happens after the function literal is type checked (which is the part that affects parameter type inference etc). So, the only way this affects a user is if they already had such an implicit conversion, which implies they would be surprised if they can no longer make that conversion trigger. As I said before, since there's no way to opt out from sam conversion, it seems implicit conversion should come first.

From a technical perspective, this two-phased approach is how type checking works in general for Scala. First you type check driven by the expected type (to do type inference etc), then you adapt to it.--

Member

adriaanm commented Mar 19, 2016

@soc, from the spec:


Translation

If the expected type of the anonymous function is of the shape scala.Function$n$[$S_1 , \ldots , S_n$, $R\,$], or can be SAM-converted to such a function type, the type $T_i$ of a parameter $x_i$ can be omitted, as far as $S_i$ is defined in the expected type, and $T_i$ = $S_i$ is assumed. Furthermore, the expected type when type checking $e$ is $R$.

[snip]

SAM conversion

An expression (p1, ..., pN) => body of function type (T1, ..., TN) => T is sam-convertible to the expected type S if the following holds:


I think this is pretty close to what you're envisioning. The only difference is that there could be an implicit that applies instead of SAM conversion, but this happens after the function literal is type checked (which is the part that affects parameter type inference etc). So, the only way this affects a user is if they already had such an implicit conversion, which implies they would be surprised if they can no longer make that conversion trigger. As I said before, since there's no way to opt out from sam conversion, it seems implicit conversion should come first.

From a technical perspective, this two-phased approach is how type checking works in general for Scala. First you type check driven by the expected type (to do type inference etc), then you adapt to it.--

@soc

This comment has been minimized.

Show comment
Hide comment
@soc

soc Mar 20, 2016

Member

Is there any real-world example which demonstrates a strong use-case for providing this feature vs. some random code snippet that might need to be changed, but will be in a better shape afterwards?

My issue is considering the SAM-thing as a conversion. Yes, it might be one from a technical perspective, but I think the special treatment of FunctionX creates distinctions where none should exist. The idea that we have function types, and then not-quite-function types attached to the sides feels deeply wrong.

Either implicit conversions should be allowed to trigger for all function types, or for none, but having two sets of function types and treating them differently feels inconsistent.

Member

soc commented Mar 20, 2016

Is there any real-world example which demonstrates a strong use-case for providing this feature vs. some random code snippet that might need to be changed, but will be in a better shape afterwards?

My issue is considering the SAM-thing as a conversion. Yes, it might be one from a technical perspective, but I think the special treatment of FunctionX creates distinctions where none should exist. The idea that we have function types, and then not-quite-function types attached to the sides feels deeply wrong.

Either implicit conversions should be allowed to trigger for all function types, or for none, but having two sets of function types and treating them differently feels inconsistent.

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Mar 21, 2016

Member

Either implicit conversions should be allowed to trigger for all function types, or for none, but having two sets of function types and treating them differently feels inconsistent.

It sounds like there may be a misunderstanding. I'm proposing implicit conversions are tried before SAM conversion, which implies "[they] trigger for all function types".

Member

adriaanm commented Mar 21, 2016

Either implicit conversions should be allowed to trigger for all function types, or for none, but having two sets of function types and treating them differently feels inconsistent.

It sounds like there may be a misunderstanding. I'm proposing implicit conversions are tried before SAM conversion, which implies "[they] trigger for all function types".

typedFunction undoes eta-expansion regardless of expected type
When recovering missing argument types for an
eta-expanded method value, rework the expected type
to a method type.
@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Mar 31, 2016

Member

@sjrd, thanks again for uncovering these bugs -- M4 will be much better for it! Could you try again and see how we're doing now? 8e32d00 should have all the fixes scala-js needs

Member

adriaanm commented Mar 31, 2016

@sjrd, thanks again for uncovering these bugs -- M4 will be much better for it! Could you try again and see how we're doing now? 8e32d00 should have all the fixes scala-js needs

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Mar 31, 2016

Member

Thanks, @retronym, for the super thorough review! 🔍 I think I've addressed all your feedback. I hope the last commit won't break anything -- I think it's optional for M4. I'll re-run the community build and hopefully we'll get the 👍 for M4 tomorrow!

Member

adriaanm commented Mar 31, 2016

Thanks, @retronym, for the super thorough review! 🔍 I think I've addressed all your feedback. I hope the last commit won't break anything -- I think it's optional for M4. I'll re-run the community build and hopefully we'll get the 👍 for M4 tomorrow!

@sjrd

This comment has been minimized.

Show comment
Hide comment
@sjrd

sjrd Mar 31, 2016

Member

@sjrd, thanks again for uncovering these bugs -- M4 will be much better for it! Could you try again and see how we're doing now? 8e32d00 should have all the fixes scala-js needs

With the latest 5d7d644, the Scala.js base (main) test suite passes :-)
I'll set up a dummy PR to test the full CI, but it should be fine.

Member

sjrd commented Mar 31, 2016

@sjrd, thanks again for uncovering these bugs -- M4 will be much better for it! Could you try again and see how we're doing now? 8e32d00 should have all the fixes scala-js needs

With the latest 5d7d644, the Scala.js base (main) test suite passes :-)
I'll set up a dummy PR to test the full CI, but it should be fine.

@sjrd

This comment has been minimized.

Show comment
Hide comment
@sjrd

sjrd Mar 31, 2016

Member

OK the Scala.js CI (mostly) passes. The only failing tests are ArrayBuilderTest.Unit_zeros_{inline,noinline} when run on the JVM, but I'm pretty sure this is unrelated to this PR. It is probably related to #4904, and it would be our job to fix our test, in that case.

So this PR is green from Scala.js' point of view.

Member

sjrd commented Mar 31, 2016

OK the Scala.js CI (mostly) passes. The only failing tests are ArrayBuilderTest.Unit_zeros_{inline,noinline} when run on the JVM, but I'm pretty sure this is unrelated to this PR. It is probably related to #4904, and it would be our job to fix our test, in that case.

So this PR is green from Scala.js' point of view.

Clarify how/when typedFunction unrolls eta-expansion
Jason points out the recursion will be okay if
type checking the function inside the eta-expansion provides
fully determined argument types, as the result type is
not relevant for this phase of typedFunction.
@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm

adriaanm Mar 31, 2016

Member

Community build is looking good! The one failure was expected.

---==  Execution Report ==---
Report from the dbuild run for dbuild: 
Project acyclic-----------------: FAILED (RuntimeException: )
Project akka--------------------: SUCCESS (unchanged, not rebuilt)
Project async-------------------: SUCCESS (unchanged, not rebuilt)
Project browse------------------: SUCCESS (unchanged, not rebuilt)
Project discipline--------------: SUCCESS (unchanged, not rebuilt)
Project fastparse---------------: DID NOT RUN (stuck on broken dependencies: acyclic)
Project genjavadoc--------------: SUCCESS (unchanged, not rebuilt)
Project genjavadoc-plugin-------: SUCCESS (unchanged, not rebuilt)
Project jawn--------------------: SUCCESS (unchanged, not rebuilt)
Project lift-json---------------: SUCCESS (unchanged, not rebuilt)
Project macro-compat------------: SUCCESS (unchanged, not rebuilt)
Project macro-paradise----------: SUCCESS (unchanged, not rebuilt)
Project mima--------------------: SUCCESS (unchanged, not rebuilt)
Project parboiled---------------: SUCCESS (unchanged, not rebuilt)
Project pimpathon---------------: SUCCESS (unchanged, not rebuilt)
Project play-twirl--------------: SUCCESS (project rebuilt ok)
Project Play2-core--------------: SUCCESS (project rebuilt ok)
Project sbinary-----------------: SUCCESS (unchanged, not rebuilt)
Project sbt---------------------: SUCCESS (unchanged, not rebuilt)
Project sbt-testng-interface----: SUCCESS (unchanged, not rebuilt)
Project scala-------------------: SUCCESS (unchanged, not rebuilt)
Project scala-js----------------: SUCCESS (project rebuilt ok)
Project scala-logging-api-------: SUCCESS (unchanged, not rebuilt)
Project scala-logging-slf4j-----: SUCCESS (unchanged, not rebuilt)
Project scala-partest-----------: SUCCESS (unchanged, not rebuilt)
Project scala-partest-interface-: SUCCESS (unchanged, not rebuilt)
Project scala-records-----------: SUCCESS (unchanged, not rebuilt)
Project scala-refactoring-------: SUCCESS (unchanged, not rebuilt)
Project scala-stm---------------: SUCCESS (unchanged, not rebuilt)
Project scala-swing-------------: SUCCESS (unchanged, not rebuilt)
Project scalacheck--------------: SUCCESS (unchanged, not rebuilt)
Project scalamock---------------: SUCCESS (unchanged, not rebuilt)
Project scalatest---------------: SUCCESS (unchanged, not rebuilt)
Project scalaz------------------: SUCCESS (unchanged, not rebuilt)
Project scalaz-stream-----------: SUCCESS (unchanged, not rebuilt)
Project scodec-bits-------------: SUCCESS (unchanged, not rebuilt)
Project scoverage---------------: SUCCESS (unchanged, not rebuilt)
Project shapeless---------------: SUCCESS (project rebuilt ok)
Project slick-------------------: SUCCESS (unchanged, not rebuilt)
Project sourcecode--------------: SUCCESS (unchanged, not rebuilt)
Project specs2_24---------------: SUCCESS (unchanged, not rebuilt)
Project specs2_36---------------: SUCCESS (project rebuilt ok)
Project spire-------------------: SUCCESS (project rebuilt ok)
Project spray-json--------------: SUCCESS (unchanged, not rebuilt)
Project spray-twirl-------------: SUCCESS (unchanged, not rebuilt)
Project twitter-util------------: SUCCESS (unchanged, not rebuilt)
Project utest-------------------: SUCCESS (project rebuilt ok)
Project zinc--------------------: SUCCESS (unchanged, not rebuilt)
>>> The dbuild result is------------: FAILED (failed: acyclic)
---==  End Execution Report ==---
Member

adriaanm commented Mar 31, 2016

Community build is looking good! The one failure was expected.

---==  Execution Report ==---
Report from the dbuild run for dbuild: 
Project acyclic-----------------: FAILED (RuntimeException: )
Project akka--------------------: SUCCESS (unchanged, not rebuilt)
Project async-------------------: SUCCESS (unchanged, not rebuilt)
Project browse------------------: SUCCESS (unchanged, not rebuilt)
Project discipline--------------: SUCCESS (unchanged, not rebuilt)
Project fastparse---------------: DID NOT RUN (stuck on broken dependencies: acyclic)
Project genjavadoc--------------: SUCCESS (unchanged, not rebuilt)
Project genjavadoc-plugin-------: SUCCESS (unchanged, not rebuilt)
Project jawn--------------------: SUCCESS (unchanged, not rebuilt)
Project lift-json---------------: SUCCESS (unchanged, not rebuilt)
Project macro-compat------------: SUCCESS (unchanged, not rebuilt)
Project macro-paradise----------: SUCCESS (unchanged, not rebuilt)
Project mima--------------------: SUCCESS (unchanged, not rebuilt)
Project parboiled---------------: SUCCESS (unchanged, not rebuilt)
Project pimpathon---------------: SUCCESS (unchanged, not rebuilt)
Project play-twirl--------------: SUCCESS (project rebuilt ok)
Project Play2-core--------------: SUCCESS (project rebuilt ok)
Project sbinary-----------------: SUCCESS (unchanged, not rebuilt)
Project sbt---------------------: SUCCESS (unchanged, not rebuilt)
Project sbt-testng-interface----: SUCCESS (unchanged, not rebuilt)
Project scala-------------------: SUCCESS (unchanged, not rebuilt)
Project scala-js----------------: SUCCESS (project rebuilt ok)
Project scala-logging-api-------: SUCCESS (unchanged, not rebuilt)
Project scala-logging-slf4j-----: SUCCESS (unchanged, not rebuilt)
Project scala-partest-----------: SUCCESS (unchanged, not rebuilt)
Project scala-partest-interface-: SUCCESS (unchanged, not rebuilt)
Project scala-records-----------: SUCCESS (unchanged, not rebuilt)
Project scala-refactoring-------: SUCCESS (unchanged, not rebuilt)
Project scala-stm---------------: SUCCESS (unchanged, not rebuilt)
Project scala-swing-------------: SUCCESS (unchanged, not rebuilt)
Project scalacheck--------------: SUCCESS (unchanged, not rebuilt)
Project scalamock---------------: SUCCESS (unchanged, not rebuilt)
Project scalatest---------------: SUCCESS (unchanged, not rebuilt)
Project scalaz------------------: SUCCESS (unchanged, not rebuilt)
Project scalaz-stream-----------: SUCCESS (unchanged, not rebuilt)
Project scodec-bits-------------: SUCCESS (unchanged, not rebuilt)
Project scoverage---------------: SUCCESS (unchanged, not rebuilt)
Project shapeless---------------: SUCCESS (project rebuilt ok)
Project slick-------------------: SUCCESS (unchanged, not rebuilt)
Project sourcecode--------------: SUCCESS (unchanged, not rebuilt)
Project specs2_24---------------: SUCCESS (unchanged, not rebuilt)
Project specs2_36---------------: SUCCESS (project rebuilt ok)
Project spire-------------------: SUCCESS (project rebuilt ok)
Project spray-json--------------: SUCCESS (unchanged, not rebuilt)
Project spray-twirl-------------: SUCCESS (unchanged, not rebuilt)
Project twitter-util------------: SUCCESS (unchanged, not rebuilt)
Project utest-------------------: SUCCESS (project rebuilt ok)
Project zinc--------------------: SUCCESS (unchanged, not rebuilt)
>>> The dbuild result is------------: FAILED (failed: acyclic)
---==  End Execution Report ==---
@retronym

This comment has been minimized.

Show comment
Hide comment
@retronym

retronym Mar 31, 2016

Member

If your project defines an implicit conversion from a SAM type
to a compatible Scala FunctionN type, this conversions will
no longer trigger

s/conversions/conversion

"trigger for a literal function"

Perhaps a short example would be helpful here.

Member

retronym commented Mar 31, 2016

If your project defines an implicit conversion from a SAM type
to a compatible Scala FunctionN type, this conversions will
no longer trigger

s/conversions/conversion

"trigger for a literal function"

Perhaps a short example would be helpful here.

@scala-jenkins scala-jenkins added reviewed and removed reviewed labels Mar 31, 2016

// The constructor only exists if the trait's template has statements.
// Sadly, we can't be more precise without access to the tree that defines the SAM's owner.
!sym.primaryConstructor.exists &&
(sym.isInterface || sym.info.decls.forall(mem => mem.isMethod || mem.isType)) // TODO OPT: && {sym setFlag INTERFACE; true})

This comment has been minimized.

@retronym

retronym Mar 31, 2016

Member

Shouldn't this be .members (now that this method doesn't recurse?)

@retronym

retronym Mar 31, 2016

Member

Shouldn't this be .members (now that this method doesn't recurse?)

This comment has been minimized.

@adriaanm

adriaanm Mar 31, 2016

Member

It still maps ok over the ancestors.

@adriaanm

adriaanm Mar 31, 2016

Member

It still maps ok over the ancestors.

@retronym

This comment has been minimized.

Show comment
Hide comment
@retronym

retronym Mar 31, 2016

Member

LGTM

Member

retronym commented Mar 31, 2016

LGTM

@adriaanm

This comment has been minimized.

Show comment
Hide comment
@adriaanm
Member

adriaanm commented Mar 31, 2016

yay

@adriaanm adriaanm merged commit 5654ebd into scala:2.12.x Mar 31, 2016

6 checks passed

cla @adriaanm signed the Scala CLA. Thanks!
Details
combined All previous commits successful.
integrate-ide [1519] SUCCESS. Took 9 s.
Details
validate-main [1748] SUCCESS. Took 64 min.
Details
validate-publish-core [1725] SUCCESS. Took 4 min.
Details
validate-test [1500] SUCCESS. Took 59 min.
Details

@SethTisue SethTisue referenced this pull request Apr 6, 2016

Closed

community build status #108

@adriaanm adriaanm deleted the adriaanm:genbcode-delambdafy branch Apr 27, 2016

adriaanm added a commit to adriaanm/community-builds that referenced this pull request May 31, 2016

@adriaanm adriaanm added 2.12.0 and removed 2.12 labels Oct 29, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment