From f922f367d58b3ba6bbb4cb0864ce82c5cd6f7966 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 17 Mar 2016 11:56:14 -0700 Subject: Additional SAM restrictions identified by Jason Also test roundtripping serialization of a lambda that targets a SAM that's not FunctionN (it should make no difference). --- test/files/neg/sammy_error_exist_no_crash.scala | 4 +- test/files/neg/sammy_restrictions.check | 55 +++++++++------------ test/files/neg/sammy_restrictions.scala | 63 ++++++++++++++----------- test/files/pos/sammy_implicit.scala | 3 +- test/files/pos/sammy_overload.scala | 27 ++++++++++- test/files/pos/sammy_poly.scala | 12 +++-- test/files/pos/sammy_scope.scala | 4 +- test/files/run/lambda-serialization.scala | 14 +++++- 8 files changed, 111 insertions(+), 71 deletions(-) (limited to 'test/files') diff --git a/test/files/neg/sammy_error_exist_no_crash.scala b/test/files/neg/sammy_error_exist_no_crash.scala index da7e47206f..667b4db763 100644 --- a/test/files/neg/sammy_error_exist_no_crash.scala +++ b/test/files/neg/sammy_error_exist_no_crash.scala @@ -1,6 +1,6 @@ -abstract class F[T] { def apply(s: T): Int } +trait F[T] { def apply(s: T): Int } object NeedsNiceError { def bar(x: F[_ >: String]) = ??? bar(_.parseInt) -} \ No newline at end of file +} diff --git a/test/files/neg/sammy_restrictions.check b/test/files/neg/sammy_restrictions.check index 0276f3a067..5fd2c858c2 100644 --- a/test/files/neg/sammy_restrictions.check +++ b/test/files/neg/sammy_restrictions.check @@ -1,46 +1,37 @@ -sammy_restrictions.scala:31: error: type mismatch; +sammy_restrictions.scala:37: error: type mismatch; found : () => Int required: NoAbstract - (() => 0) : NoAbstract + (() => 0) : NoAbstract // error expected ^ -sammy_restrictions.scala:32: error: type mismatch; - found : Int => Int - required: TwoAbstract - ((x: Int) => 0): TwoAbstract - ^ -sammy_restrictions.scala:35: error: type mismatch; - found : Int => Int - required: NoEmptyConstructor - ((x: Int) => 0): NoEmptyConstructor - ^ -sammy_restrictions.scala:37: error: type mismatch; - found : Int => Int - required: OneEmptySecondaryConstructor - ((x: Int) => 0): OneEmptySecondaryConstructor // derived class must have an empty *primary* to call. - ^ sammy_restrictions.scala:38: error: type mismatch; found : Int => Int - required: MultipleConstructorLists - ((x: Int) => 0): MultipleConstructorLists + required: TwoAbstract + ((x: Int) => 0): TwoAbstract // error expected ^ -sammy_restrictions.scala:39: error: type mismatch; +sammy_restrictions.scala:41: error: type mismatch; found : Int => Int required: MultipleMethodLists - ((x: Int) => 0): MultipleMethodLists + ((x: Int) => 0): MultipleMethodLists // error expected ^ -sammy_restrictions.scala:40: error: type mismatch; - found : Int => Int - required: ImplicitConstructorParam - ((x: Int) => 0): ImplicitConstructorParam - ^ -sammy_restrictions.scala:41: error: type mismatch; +sammy_restrictions.scala:42: error: type mismatch; found : Int => Int required: ImplicitMethodParam - ((x: Int) => 0): ImplicitMethodParam + ((x: Int) => 0): ImplicitMethodParam // error expected ^ -sammy_restrictions.scala:44: error: type mismatch; +sammy_restrictions.scala:45: error: type mismatch; found : Int => Int required: PolyMethod - ((x: Int) => 0): PolyMethod - ^ -9 errors found + ((x: Int) => 0): PolyMethod // error expected + ^ +sammy_restrictions.scala:47: error: missing parameter type + (x => x + 1): NotAnInterface[Int, Int] // error expected (not an interface) + ^ +sammy_restrictions.scala:48: error: type mismatch; + found : String => Int + required: A[Object,Int] + ((x: String) => 1): A[Object, Int] // error expected (type mismatch) + ^ +sammy_restrictions.scala:51: error: missing parameter type + n.app(1)(x => List(x)) // error expected: n.F is not a SAM type (it does not have a no-arg ctor since it has an outer pointer) + ^ +8 errors found diff --git a/test/files/neg/sammy_restrictions.scala b/test/files/neg/sammy_restrictions.scala index 101342ad0b..ed8cf35aa4 100644 --- a/test/files/neg/sammy_restrictions.scala +++ b/test/files/neg/sammy_restrictions.scala @@ -1,45 +1,52 @@ -abstract class NoAbstract +trait NoAbstract -abstract class TwoAbstract { def ap(a: Int): Int; def pa(a: Int): Int } +trait TwoAbstract { def ap(a: Int): Int; def pa(a: Int): Int } -abstract class Base // check that the super class constructor isn't considered. -abstract class NoEmptyConstructor(a: Int) extends Base { def this(a: String) = this(0); def ap(a: Int): Int } +trait Base // check that the super class constructor isn't considered. -abstract class OneEmptyConstructor() { def this(a: Int) = this(); def ap(a: Int): Int } +trait MultipleMethodLists { def ap(a: Int)(): Int } -abstract class OneEmptySecondaryConstructor(a: Int) { def this() = this(0); def ap(a: Int): Int } +trait ImplicitMethodParam { def ap(a: Int)(implicit b: String): Int } -abstract class MultipleConstructorLists()() { def ap(a: Int): Int } +trait PolyClass[T] { def ap(a: T): T } -abstract class MultipleMethodLists()() { def ap(a: Int)(): Int } +trait PolyMethod { def ap[T](a: T): T } -abstract class ImplicitConstructorParam()(implicit a: String) { def ap(a: Int): Int } +trait OneAbstract { def ap(a: Int): Any } +trait DerivedOneAbstract extends OneAbstract -abstract class ImplicitMethodParam() { def ap(a: Int)(implicit b: String): Int } +// restrictions -abstract class PolyClass[T] { def ap(a: T): T } +// must be an interface +abstract class NotAnInterface[T, R]{ def apply(x: T): R } -abstract class PolyMethod { def ap[T](a: T): T } +trait A[T, R]{ def apply(x: T): R } + +// must not capture +class Nested { + trait F[T, U] { def apply(x: T): U } + + def app[T, U](x: T)(f: F[T, U]): U = f(x) +} -abstract class OneAbstract { def ap(a: Int): Any } -abstract class DerivedOneAbstract extends OneAbstract object Test { implicit val s: String = "" type NonClassType = DerivedOneAbstract with OneAbstract - (() => 0) : NoAbstract - ((x: Int) => 0): TwoAbstract - ((x: Int) => 0): DerivedOneAbstract // okay - ((x: Int) => 0): NonClassType // okay -- we also allow type aliases in instantiation expressions, if they resolve to a class type - ((x: Int) => 0): NoEmptyConstructor - ((x: Int) => 0): OneEmptyConstructor // okay - ((x: Int) => 0): OneEmptySecondaryConstructor // derived class must have an empty *primary* to call. - ((x: Int) => 0): MultipleConstructorLists - ((x: Int) => 0): MultipleMethodLists - ((x: Int) => 0): ImplicitConstructorParam - ((x: Int) => 0): ImplicitMethodParam - - ((x: Int) => 0): PolyClass[Int] // okay - ((x: Int) => 0): PolyMethod + (() => 0) : NoAbstract // error expected + ((x: Int) => 0): TwoAbstract // error expected + ((x: Int) => 0): DerivedOneAbstract + ((x: Int) => 0): NonClassType + ((x: Int) => 0): MultipleMethodLists // error expected + ((x: Int) => 0): ImplicitMethodParam // error expected + + ((x: Int) => 0): PolyClass[Int] + ((x: Int) => 0): PolyMethod // error expected + + (x => x + 1): NotAnInterface[Int, Int] // error expected (not an interface) + ((x: String) => 1): A[Object, Int] // error expected (type mismatch) + + val n = new Nested + n.app(1)(x => List(x)) // error expected: n.F is not a SAM type (it does not have a no-arg ctor since it has an outer pointer) } diff --git a/test/files/pos/sammy_implicit.scala b/test/files/pos/sammy_implicit.scala index e4b82df4cc..ab63fc729e 100644 --- a/test/files/pos/sammy_implicit.scala +++ b/test/files/pos/sammy_implicit.scala @@ -1,5 +1,6 @@ +trait Fun[A, B] { def apply(a: A): B } + abstract class SamImplicitConvert { - trait Fun[A, B] { def apply(a: A): B } class Lst[T] abstract class Str { def getBytes: Array[Int] } def flatMap[B](f: Fun[Str, Lst[B]]): List[B] = ??? diff --git a/test/files/pos/sammy_overload.scala b/test/files/pos/sammy_overload.scala index 5472248f4d..6a3c88ec55 100644 --- a/test/files/pos/sammy_overload.scala +++ b/test/files/pos/sammy_overload.scala @@ -6,4 +6,29 @@ object Test { def foo(x: String): Unit = ??? def foo(): Unit = ??? val f: Consumer[_ >: String] = foo -} \ No newline at end of file +} + +trait A[A, B] { def apply(a: A): B } + +class ArityDisambiguate { + object O { + def m(a: A[Int, Int]) = 0 + def m(f: (Int, Int) => Int) = 1 + } + + O.m(x => x) // ok + O.m((x, y) => x) // ok +} + +class InteractionWithImplicits { + object O { + class Ev + implicit object E1 extends Ev + implicit object E2 extends Ev + def m(a: A[Int, Int])(implicit ol: E1.type) = 0 + def m(a: A[String, Int])(implicit ol: E2.type) = 1 + } + + O.m((x: Int) => 1) // ok + O.m((x: String) => 1) // ok +} diff --git a/test/files/pos/sammy_poly.scala b/test/files/pos/sammy_poly.scala index 75ee36f654..ba10baea49 100644 --- a/test/files/pos/sammy_poly.scala +++ b/test/files/pos/sammy_poly.scala @@ -1,8 +1,12 @@ // test synthesizeSAMFunction where the sam type is not fully defined -class T { - trait F[T, U] { def apply(x: T): U } -// type F[T, U] = T => U - // NOTE: the f(x) desugaring for now assumes the single abstract method is called 'apply' +trait F[T, R]{ def apply(x: T): R } + +class PolySammy { + (x => x + 1): F[Int, Int] + ((x: Int) => x + 1): F[Int, Int] + ((x: String) => 1): F[String, Int] + ((x: Object) => 1): F[String, Int] + def app[T, U](x: T)(f: F[T, U]): U = f(x) app(1)(x => List(x)) } diff --git a/test/files/pos/sammy_scope.scala b/test/files/pos/sammy_scope.scala index 8f1fe7058e..9d35501a47 100644 --- a/test/files/pos/sammy_scope.scala +++ b/test/files/pos/sammy_scope.scala @@ -1,8 +1,8 @@ // test synthesizeSAMFunction: scope hygiene -abstract class SamFun[T1, R] { self => +trait SamFun[T1, R] { self => def apply(v1: T1): R // this should type check, as the apply ref is equivalent to self.apply // it shouldn't resolve to the sam's apply that's synthesized (that wouldn't type check, hence the pos test) def compose[A](g: SamFun[A, T1]): SamFun[A, R] = { x => apply(g(x)) } -} \ No newline at end of file +} diff --git a/test/files/run/lambda-serialization.scala b/test/files/run/lambda-serialization.scala index 46b26d7c5e..0eee1193d7 100644 --- a/test/files/run/lambda-serialization.scala +++ b/test/files/run/lambda-serialization.scala @@ -1,8 +1,11 @@ import java.io.{ByteArrayInputStream, ObjectInputStream, ObjectOutputStream, ByteArrayOutputStream} +trait IntToString { def apply(i: Int): String } + object Test { def main(args: Array[String]): Unit = { - roundTrip + roundTrip() + roundTripIndySam() } def roundTrip(): Unit = { @@ -22,6 +25,15 @@ object Test { assert(serializeDeserialize(serializeDeserialize(specializedLambda)).apply(42) == 2) } + // lambda targeting a SAM, not a FunctionN (should behave the same way) + def roundTripIndySam(): Unit = { + val lambda: IntToString = (x: Int) => "yo!" * x + val reconstituted1 = serializeDeserialize(lambda).asInstanceOf[IntToString] + val reconstituted2 = serializeDeserialize(reconstituted1).asInstanceOf[IntToString] + assert(reconstituted1.apply(2) == "yo!yo!") + assert(reconstituted1.getClass == reconstituted2.getClass) + } + def serializeDeserialize[T <: AnyRef](obj: T) = { val buffer = new ByteArrayOutputStream val out = new ObjectOutputStream(buffer) -- cgit v1.2.3