Skip to content

Commit 951b05f

Browse files
authored
Merge pull request #7360 from neshkeev/wip/7359-fix-sam-processing
Fixes #7359 - Fix SAM processing
2 parents 87ea7a6 + f183cc0 commit 951b05f

21 files changed

+269
-5
lines changed

compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,8 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
846846
def addRemoteRemoteExceptionAnnotation: Unit = ()
847847

848848
def samMethod(): Symbol = ctx.atPhase(ctx.erasurePhase) {
849-
toDenot(sym).info.abstractTermMembers.toList match {
849+
val samMethods = toDenot(sym).info.possibleSamMethods.toList
850+
samMethods match {
850851
case x :: Nil => x.symbol
851852
case Nil => abort(s"${sym.show} is not a functional interface. It doesn't have abstract methods")
852853
case xs => abort(s"${sym.show} is not a functional interface. " +

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

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ import java.lang.ref.WeakReference
3737
import scala.annotation.internal.sharable
3838
import scala.annotation.threadUnsafe
3939

40+
import dotty.tools.dotc.transform.SymUtils._
41+
4042
object Types {
4143

4244
@sharable private var nextId = 0
@@ -767,6 +769,26 @@ object Types {
767769
(name, buf) => buf ++= nonPrivateMember(name).altsWith(_.is(Deferred)))
768770
}
769771

772+
/**
773+
* Returns the set of methods that are abstract and do not overlap with any of
774+
* [[java.lang.Object]] methods.
775+
*
776+
* Conceptually, a SAM (functional interface) has exactly one abstract method.
777+
* If an interface declares an abstract method overriding one of the public
778+
* methods of [[java.lang.Object]], that also does not count toward the interface's
779+
* abstract method count.
780+
*
781+
* @see https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html
782+
*
783+
* @return the set of methods that are abstract and do not match any of [[java.lang.Object]]
784+
*
785+
*/
786+
final def possibleSamMethods(implicit ctx: Context): Seq[SingleDenotation] = {
787+
record("possibleSamMethods")
788+
abstractTermMembers
789+
.filterNot(m => m.symbol.matchingMember(defn.ObjectType).exists || m.symbol.isSuperAccessor)
790+
}
791+
770792
/** The set of abstract type members of this type. */
771793
final def abstractTypeMembers(implicit ctx: Context): Seq[SingleDenotation] = {
772794
record("abstractTypeMembers")
@@ -4369,8 +4391,7 @@ object Types {
43694391
}
43704392
def unapply(tp: Type)(implicit ctx: Context): Option[MethodType] =
43714393
if (isInstantiatable(tp)) {
4372-
val absMems = tp.abstractTermMembers
4373-
// println(s"absMems: ${absMems map (_.show) mkString ", "}")
4394+
val absMems = tp.possibleSamMethods
43744395
if (absMems.size == 1)
43754396
absMems.head.info match {
43764397
case mt: MethodType if !mt.isParamDependent &&

compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class ExpandSAMs extends MiniPhase {
4646
tree
4747
case tpe =>
4848
val tpe1 = checkRefinements(tpe, fn)
49-
val Seq(samDenot) = tpe1.abstractTermMembers.filter(!_.symbol.isSuperAccessor)
49+
val Seq(samDenot) = tpe1.possibleSamMethods
5050
cpy.Block(tree)(stats,
5151
AnonClass(tpe1 :: Nil, fn.symbol.asTerm :: Nil, samDenot.symbol.asTerm.name :: Nil))
5252
}

project/Build.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -979,7 +979,7 @@ object Build {
979979
++ (dir / "shared/src/test/scala/org/scalajs/testsuite/niobuffer" ** (("*.scala": FileFilter) -- "ByteBufferTest.scala")).get
980980
++ (dir / "shared/src/test/scala/org/scalajs/testsuite/niocharset" ** (("*.scala": FileFilter) -- "BaseCharsetTest.scala" -- "Latin1Test.scala" -- "USASCIITest.scala" -- "UTF16Test.scala" -- "UTF8Test.scala")).get
981981
++ (dir / "shared/src/test/scala/org/scalajs/testsuite/scalalib" ** (("*.scala": FileFilter) -- "ArrayBuilderTest.scala" -- "ClassTagTest.scala" -- "EnumerationTest.scala" -- "RangesTest.scala" -- "SymbolTest.scala")).get
982-
++ (dir / "shared/src/test/require-sam" ** (("*.scala": FileFilter) -- "SAMTest.scala")).get
982+
++ (dir / "shared/src/test/require-sam" ** "*.scala").get
983983
++ (dir / "shared/src/test/require-jdk8/org/scalajs/testsuite/compiler" ** (("*.scala": FileFilter) -- "DefaultMethodsTest.scala")).get
984984
++ (dir / "shared/src/test/require-jdk8/org/scalajs/testsuite/javalib/lang" ** "*.scala").get
985985
++ (dir / "shared/src/test/require-jdk8/org/scalajs/testsuite/javalib/util" ** (("*.scala": FileFilter) -- "CollectionsOnCopyOnWriteArrayListTestOnJDK8.scala")).get

tests/neg/i7359-b.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- [E105] Syntax Error: tests/neg/i7359-b.scala:3:6 --------------------------------------------------------------------
2+
3 | def notifyAll(): Unit // error
3+
| ^
4+
| Traits cannot redefine final method notifyAll from class AnyRef.

tests/neg/i7359-b.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
trait SAMTrait
2+
def first(): String
3+
def notifyAll(): Unit // error

tests/neg/i7359-c.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- [E105] Syntax Error: tests/neg/i7359-c.scala:3:6 --------------------------------------------------------------------
2+
3 | def wait(): Unit // error
3+
| ^
4+
| Traits cannot redefine final method wait from class AnyRef.

tests/neg/i7359-c.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
trait SAMTrait
2+
def first(): String
3+
def wait(): Unit // error

tests/neg/i7359-d.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- [E105] Syntax Error: tests/neg/i7359-d.scala:3:6 --------------------------------------------------------------------
2+
3 | def wait(a: Long): Unit // error
3+
| ^
4+
| Traits cannot redefine final method wait from class AnyRef.

tests/neg/i7359-d.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
trait SAMTrait
2+
def first(): String
3+
def wait(a: Long): Unit // error

tests/neg/i7359-e.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- [E105] Syntax Error: tests/neg/i7359-e.scala:3:6 --------------------------------------------------------------------
2+
3 | def wait(a: Long, n: Int): Unit // error
3+
| ^
4+
| Traits cannot redefine final method wait from class AnyRef.

tests/neg/i7359-e.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
trait SAMTrait
2+
def first(): String
3+
def wait(a: Long, n: Int): Unit // error

tests/neg/i7359-f.check

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- [E120] Duplicate Symbol Error: tests/neg/i7359-f.scala:1:6 ----------------------------------------------------------
2+
1 |trait SAMTrait // error
3+
| ^
4+
| Name clash between inherited members:
5+
| def equals: [T >: Boolean <: Boolean](obj: Any): T in trait SAMTrait at line 3 and
6+
| def equals(x$0: Any): Boolean in class Any
7+
| have the same type after erasure.

tests/neg/i7359-f.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
trait SAMTrait // error
2+
def first(): String
3+
def equals[T >: Boolean <: Boolean](obj: Any): T

tests/neg/i7359-g.check

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- [E007] Type Mismatch Error: tests/neg/i7359-g.scala:5:25 ------------------------------------------------------------
2+
5 |val m : SAMTrait = () => "Hello" // error
3+
| ^^^^^^^
4+
| Found: () => String
5+
| Required: SAMTrait

tests/neg/i7359-g.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
trait SAMTrait
2+
def first(): String
3+
def equals(obj: Int): Boolean
4+
5+
val m : SAMTrait = () => "Hello" // error

tests/neg/i7359.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- [E105] Syntax Error: tests/neg/i7359.scala:3:6 ----------------------------------------------------------------------
2+
3 | def notify(): Unit // error
3+
| ^
4+
| Traits cannot redefine final method notify from class AnyRef.

tests/neg/i7359.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
trait SAMTrait
2+
def first(): String
3+
def notify(): Unit // error

tests/pos/i7359.scala

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package test
2+
3+
trait ObjectInterface
4+
def equals(obj: Any): Boolean
5+
def hashCode(): Int
6+
def toString(): String
7+
8+
trait SAMPlain
9+
def first(): String
10+
11+
trait SAMPlainWithOverriddenObjectMethods
12+
def first(): String
13+
def equals(obj: Any): Boolean
14+
def hashCode(): Int
15+
def toString(): String
16+
17+
trait SAMPlainWithExtends extends ObjectInterface
18+
def first(): String
19+
20+
trait SAMPlainWithExtendsAndOverride extends ObjectInterface
21+
def first(): String
22+
override def equals(obj: Any): Boolean
23+
override def hashCode(): Int
24+
override def toString(): String
25+
26+
trait SAMPlainCovariantOut[+O]
27+
def first(): O
28+
29+
trait SAMCovariantOut[+O]
30+
def first(): O
31+
def equals(obj: Any): Boolean
32+
def hashCode(): Int
33+
def toString(): String
34+
35+
trait SAMCovariantOutExtends[+O] extends ObjectInterface
36+
def first(): O
37+
38+
trait SAMCovariantOutExtendsAndOverride[+O] extends ObjectInterface
39+
def first(): O
40+
override def equals(obj: Any): Boolean
41+
override def hashCode(): Int
42+
override def toString(): String
43+
44+
trait SAMPlainContravariantIn[-I]
45+
def first(in: I): Unit
46+
47+
trait SAMContravariantIn[-I]
48+
def first(in: I): Unit
49+
def equals(obj: Any): Boolean
50+
def hashCode(): Int
51+
def toString(): String
52+
53+
trait SAMContravariantInExtends[-I] extends ObjectInterface
54+
def first(in: I): Unit
55+
56+
trait SAMContravariantInExtendsAndOverride[-I] extends ObjectInterface
57+
def first(in: I): Unit
58+
override def equals(obj: Any): Boolean
59+
override def hashCode(): Int
60+
override def toString(): String
61+
62+
trait SAMPlainInvariant[T]
63+
def first(in: T): T
64+
65+
trait SAMInvariant[T]
66+
def first(in: T): T
67+
def equals(obj: Any): Boolean
68+
def hashCode(): Int
69+
def toString(): String
70+
71+
trait SAMInvariantExtends[T] extends ObjectInterface
72+
def first(in: T): T
73+
74+
trait SAMInvariantExtendsAndOverride[T] extends ObjectInterface
75+
def first(in: T): T
76+
override def equals(obj: Any): Boolean
77+
override def hashCode(): Int
78+
override def toString(): String
79+
80+
trait SAMPlainInOut[-I, +O]
81+
def first(in: I): O
82+
83+
trait SAMInOut[-I, +O]
84+
def first(in: I): O
85+
def equals(obj: Any): Boolean
86+
def hashCode(): Int
87+
def toString(): String
88+
89+
trait SAMInOutExtends[-I, +O] extends ObjectInterface
90+
def first(in: I): O
91+
92+
trait SAMInOutExtendsAndOverride[-I, +O] extends ObjectInterface
93+
def first(in: I): O
94+
override def equals(obj: Any): Boolean
95+
override def hashCode(): Int
96+
override def toString(): String
97+
98+
type CustomString = String
99+
type CustomBoolean = Boolean
100+
type CustomInt = Int
101+
102+
trait SAMWithCustomAliases
103+
def first(): String
104+
def equals(obj: Any): CustomBoolean
105+
def hashCode(): CustomInt
106+
def toString(): CustomString
107+
108+
object Main
109+
def main(args: Array[String]) =
110+
val samPlain : SAMPlain = () => "Hello, World!"
111+
val samPlainWithOverriddenObjectMethods: SAMPlainWithOverriddenObjectMethods = () => "Hello, World!"
112+
val samPlainWithExtends : SAMPlainWithExtends = () => "Hello, World!"
113+
val samPlainWithExtendsAndOverride : SAMPlainWithExtendsAndOverride = () => "Hello, World!"
114+
115+
val samPlainCovariantOut : SAMPlainCovariantOut[_] = () => "Hello, World!"
116+
val samCovariantOut : SAMCovariantOut[_] = () => "Hello, World!"
117+
val samCovariantOutExtends : SAMCovariantOutExtends[_] = () => "Hello, World!"
118+
val samCovariantOutExtendsAndOverride : SAMCovariantOutExtendsAndOverride[_] = () => "Hello, World!"
119+
120+
val samPlainContravariantIn : SAMPlainContravariantIn[Int] = (x: Int) => ()
121+
val samContravariantIn : SAMContravariantIn[Int] = (x: Int) => ()
122+
val samContravariantInExtends : SAMContravariantInExtends[Int] = (x: Int) => ()
123+
val samContravariantInExtendsAndOverride : SAMContravariantInExtendsAndOverride[Int] = (x: Int) => ()
124+
125+
val samPlainInvariant : SAMPlainInvariant[String] = (x: String) => x
126+
val samInvariant : SAMInvariant[String] = (x: String) => x
127+
val samInvariantExtends : SAMInvariantExtends[String] = (x: String) => x
128+
val samInvariantExtendsAndOverride : SAMInvariantExtendsAndOverride[String] = (x: String) => x
129+
130+
val samPlainInOut : SAMPlainInOut[Int, String] = (x: Int) => x.toString
131+
val samInOut : SAMInOut[Int, String] = (x: Int) => x.toString
132+
val samInOutExtends : SAMInOutExtends[Int, String] = (x: Int) => x.toString
133+
val samInOutExtendsAndOverride : SAMInOutExtendsAndOverride[Int, String] = (x: Int) => x.toString
134+
135+
val samWithCustomAliases : SAMWithCustomAliases = () => "Hello, World!"

tests/run/i7359.check

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
o
2+
o
3+
()
4+
i
5+
10
6+
o

tests/run/i7359.scala

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
trait ObjectInterface
2+
def equals(obj: Any): Boolean
3+
def hashCode(): Int
4+
def toString(): String
5+
6+
trait SAMPlainWithExtends extends ObjectInterface
7+
def first(): String
8+
9+
trait SAMCovariantOutExtends[+O] extends ObjectInterface
10+
def first(): O
11+
12+
trait SAMContravariantInExtends[-I] extends ObjectInterface
13+
def first(in: I): Unit
14+
15+
trait SAMInvariantExtends[T] extends ObjectInterface
16+
def first(in: T): T
17+
18+
trait SAMInOutExtends[-I, +O] extends ObjectInterface
19+
def first(in: I): O
20+
21+
type CustomString = String
22+
type CustomBoolean = Boolean
23+
type CustomInt = Int
24+
25+
trait SAMWithCustomAliases
26+
def first(): String
27+
def equals(obj: Any): CustomBoolean
28+
def hashCode(): CustomInt
29+
def toString(): CustomString
30+
31+
object Test
32+
33+
def main(args: Array[String]): Unit =
34+
val samPlainWithExtends : SAMPlainWithExtends = () => "o"
35+
val samCovariantOutExtends : SAMCovariantOutExtends[_] = () => "o"
36+
val samContravariantInExtends : SAMContravariantInExtends[Int] = (x: Int) => ()
37+
val samInvariantExtends : SAMInvariantExtends[String] = (x: String) => x
38+
val samInOutExtends : SAMInOutExtends[Int, String] = (x: Int) => x.toString
39+
val samWithCustomAliases : SAMWithCustomAliases = () => "o"
40+
41+
println(samPlainWithExtends.first())
42+
println(samCovariantOutExtends.first())
43+
println(samContravariantInExtends.first(10))
44+
println(samInvariantExtends.first("i"))
45+
println(samInOutExtends.first(10))
46+
println(samWithCustomAliases.first())

0 commit comments

Comments
 (0)