Skip to content

Commit 1aff0f9

Browse files
committed
Fix #4611:
class and trait with a single abstract method which result type is an implicit function type are not considered SAM types anymore. The reason is because the abstract member desugar to mutiple ones. E.g. ```scala trait Foo { def foo(): implicit Int => Int } // becomes trait Foo { def foo(): implicit Int => Int def foo$direct(implicit x: Int): Int } ```
1 parent 2e53688 commit 1aff0f9

File tree

4 files changed

+87
-2
lines changed

4 files changed

+87
-2
lines changed

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3835,7 +3835,7 @@ object Types {
38353835
* A type is a SAM type if it is a reference to a class or trait, which
38363836
*
38373837
* - has a single abstract method with a method type (ExprType
3838-
* and PolyType not allowed!)
3838+
* and PolyType not allowed!) which result type is not an implicit function type
38393839
* - can be instantiated without arguments or with just () as argument.
38403840
*
38413841
* The pattern `SAMType(sam)` matches a SAM type, where `sam` is the
@@ -3877,7 +3877,8 @@ object Types {
38773877
// println(s"absMems: ${absMems map (_.show) mkString ", "}")
38783878
if (absMems.size == 1)
38793879
absMems.head.info match {
3880-
case mt: MethodType if !mt.isParamDependent =>
3880+
case mt: MethodType if !mt.isParamDependent &&
3881+
!defn.isImplicitFunctionType(mt.resultType) =>
38813882
val cls = tp.classSymbol
38823883

38833884
// Given a SAM type such as:

tests/neg/i4611a.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Don't qualify as SAM type because result type is an implicit function type
2+
trait Foo {
3+
def foo(x: Int): implicit Int => Int
4+
}
5+
6+
class Test {
7+
val good = new Foo {
8+
def foo(x: Int) = 1
9+
}
10+
11+
val bad: Foo = (x: Int) => 1 // error
12+
}

tests/neg/i4611b.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import scala.concurrent.Future
2+
3+
class Response
4+
class Request
5+
object Request {
6+
type To[T] = implicit Request => T
7+
}
8+
9+
// Don't qualify as SAM type because result type is an implicit function type
10+
trait Responder[T] {
11+
def responseFor(value: T): Request.To[Future[Response]]
12+
}
13+
14+
object Responder {
15+
// with SAM
16+
val responseResponder: Responder[Response] =
17+
response => Future.successful(response) // error
18+
19+
// with anonymous class
20+
val futureResponseResponder: Responder[Future[Response]] = new Responder[Future[Response]] {
21+
override def responseFor(value: Future[Response]): Request.To[Future[Response]] =
22+
value
23+
}
24+
}

tests/run/implicitFuns2.scala

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
class A
2+
class B
3+
4+
trait Foo {
5+
def foo: implicit A => implicit B => Int
6+
}
7+
8+
class Foo1 extends Foo {
9+
def foo: implicit A => implicit B => Int = 1
10+
}
11+
12+
class Foo2 extends Foo1 {
13+
override def foo: implicit A => implicit B => Int = 2
14+
}
15+
16+
trait Foo3 extends Foo {
17+
override def foo: implicit A => implicit B => Int = 3
18+
}
19+
20+
class Bar[T] {
21+
def bar: implicit A => T = null.asInstanceOf[T]
22+
}
23+
24+
class Bar1 extends Bar[implicit B => Int] {
25+
override def bar: implicit A => implicit B => Int = 1
26+
}
27+
28+
object Test {
29+
def testFoo() = {
30+
implicit val a = new A
31+
implicit val b = new B
32+
assert((new Foo1).foo == 1)
33+
assert((new Foo2).foo == 2)
34+
assert(new Foo3{}.foo == 3)
35+
}
36+
37+
def testBar() = {
38+
implicit val a = new A
39+
implicit val b = new B
40+
assert((new Bar).bar == null)
41+
assert((new Bar1).bar == 1)
42+
}
43+
44+
def main(args: Array[String]): Unit = {
45+
testFoo()
46+
testBar()
47+
}
48+
}

0 commit comments

Comments
 (0)