Skip to content

Commit 35a8c53

Browse files
committed
Check erased SAM method signature compatibility
1 parent 46ae660 commit 35a8c53

File tree

3 files changed

+29
-5
lines changed

3 files changed

+29
-5
lines changed

compiler/src/dotty/tools/dotc/config/JavaPlatform.scala

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import io.*
66
import classpath.AggregateClassPath
77
import core.*
88
import Symbols.*, Types.*, Contexts.*, StdNames.*
9+
import Phases.*
910
import Flags.*
1011
import transform.ExplicitOuter
1112

@@ -44,6 +45,17 @@ class JavaPlatform extends Platform {
4445

4546
def rootLoader(root: TermSymbol)(using Context): SymbolLoader = new SymbolLoaders.PackageLoader(root, classPath)
4647

48+
private def samMethodHasCompatibleErasedSignature(cls: ClassSymbol)(using Context): Boolean = atPhase(erasurePhase):
49+
cls.typeRef.possibleSamMethods.toList match
50+
case samDenot :: Nil =>
51+
val samMethod = samDenot.symbol
52+
val samErasedResult = TypeErasure.erasure(samMethod.info.resultType)
53+
samMethod.allOverriddenSymbols.forall { overridden =>
54+
val overriddenErasedResult = TypeErasure.erasure(overridden.info.resultType)
55+
samErasedResult =:= overriddenErasedResult
56+
}
57+
case _ => false // No SAM method or multiple - handled elsewhere
58+
4759
/** Is the SAMType `cls` also a SAM under the rules of the JVM? */
4860
def isSam(cls: ClassSymbol)(using Context): Boolean =
4961
cls.isAllOf(NoInitsTrait) &&
@@ -52,8 +64,10 @@ class JavaPlatform extends Platform {
5264
!ExplicitOuter.needsOuterIfReferenced(cls) &&
5365
// Superaccessors already show up as abstract methods here, so no test necessary
5466
cls.typeRef.fields.isEmpty &&
55-
// LambdaMetafactory can't handle SAMs with Unit return type unless it's a FunctionN itself
56-
!cls.typeRef.possibleSamMethods.exists(_.info.resultType.isRef(defn.UnitClass))
67+
// Check that SAM method's erased signature is compatible with all overridden methods
68+
// For example, `void apply(Object)` is not compatible with `Object apply(Object)`
69+
// even though both can have the same type signature `def apply(o: Object): Unit` before erasure.
70+
samMethodHasCompatibleErasedSignature(cls)
5771

5872
/** We could get away with excluding BoxedBooleanClass for the
5973
* purpose of equality testing since it need not compare equal

tests/run/i24573.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
42
55
hello
66
world
7+
!!

tests/run/i24573.scala

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,17 @@ trait Con2[-T] extends (T => Int):
77
trait Con3[+R] extends (() => R):
88
def apply(): R
99

10-
trait F1[-T, +R] {
10+
trait F1[-T, +R]:
1111
def apply(t: T): R
12-
}
1312

14-
trait SF[-T] extends F1[T, Unit] { def apply(t: T): Unit }
13+
trait SF[-T] extends F1[T, Unit]:
14+
def apply(t: T): Unit
15+
16+
trait F1U[-T]:
17+
def apply(t: T): Unit
18+
19+
trait SF2 extends F1U[String]:
20+
def apply(t: String): Unit
1521

1622
object Test:
1723
def main(args: Array[String]): Unit =
@@ -32,3 +38,6 @@ object Test:
3238

3339
val f5: SF[String] = s => println(s)
3440
f5("world")
41+
42+
val f6: SF2 = i => println(i)
43+
f6("!!")

0 commit comments

Comments
 (0)