Skip to content

Commit d10f173

Browse files
committed
Move Scala.js array adaptation fix from tpd.AnonClass to ExpandSAMs
The fix for #23179 is now localized to ExpandSAMs where the SAM expansion happens, rather than in the general-purpose tpd.AnonClass function.
1 parent 2c7b12f commit d10f173

File tree

2 files changed

+48
-36
lines changed

2 files changed

+48
-36
lines changed

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -394,38 +394,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
394394
adaptVarargs: Boolean)(using Context): Block = {
395395
AnonClass(termForwarders.head._2.owner, parents, termForwarders.map(_._2.span).reduceLeft(_ `union` _), adaptVarargs) { cls =>
396396
def forwarder(name: TermName, fn: TermSymbol) = {
397-
// Look up the SAM method from the parent to get its signature
398-
val samMember = parents.head.member(name)
399-
// Use SAM's signature if it exists and the SAM param erases to an array but the fn param
400-
// does not. This handles cases like Array[? >: AnyRef] (erases to Object) vs
401-
// Array[AnyRef] (erases to Array[Object]).
402-
val useParentSig = samMember.exists && {
403-
val samInfo = samMember.info
404-
val fnInfo = fn.info
405-
(samInfo, fnInfo) match
406-
case (samMt: MethodType, fnMt: MethodType) =>
407-
samMt.paramInfos.lazyZip(fnMt.paramInfos).exists { (samPt, fnPt) =>
408-
val samErased = TypeErasure.erasure(samPt)
409-
val fnErased = TypeErasure.erasure(fnPt)
410-
// Only adapt when SAM expects an array but fn takes Object
411-
samErased.isInstanceOf[JavaArrayType] && !fnErased.isInstanceOf[JavaArrayType]
412-
}
413-
case _ => false
414-
}
415-
val fwdMeth =
416-
if useParentSig then
417-
newSymbol(cls, name, Synthetic | Method | Final | Override, samMember.info).entered.asTerm
418-
else
419-
fn.copy(cls, name, Synthetic | Method | Final).entered.asTerm
397+
val fwdMeth = fn.copy(cls, name, Synthetic | Method | Final).entered.asTerm
420398
for overridden <- fwdMeth.allOverriddenSymbols do
421399
if overridden.is(Extension) then fwdMeth.setFlag(Extension)
422400
if !overridden.is(Deferred) then fwdMeth.setFlag(Override)
423-
if useParentSig then
424-
// Need to cast args from SAM param types to fn param types
425-
DefDef(fwdMeth, argss =>
426-
ref(fn).appliedToArgss(argss.map(_.map(arg => arg.cast(arg.tpe.widen)))))
427-
else
428-
DefDef(fwdMeth, ref(fn).appliedToArgss(_))
401+
DefDef(fwdMeth, ref(fn).appliedToArgss(_))
429402
}
430403
termForwarders.map((name, sym) => forwarder(name, sym)) ++
431404
typeMembers.map((name, info) => TypeDef(newSymbol(cls, name, Synthetic, info).entered))

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

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dotc
33
package transform
44

55
import core.*
6+
import core.TypeErasure.erasure
67
import Scopes.newScope
78
import Contexts.*, Symbols.*, Types.*, Flags.*, Decorators.*, StdNames.*, Constants.*
89
import MegaPhase.*
@@ -69,14 +70,52 @@ class ExpandSAMs extends MiniPhase:
6970
case _ => tp
7071
val tpe1 = collectAndStripRefinements(tpe)
7172
val Seq(samDenot) = tpe1.possibleSamMethods
72-
cpy.Block(tree)(stats,
73-
transformFollowingDeep:
74-
AnonClass(List(tpe1),
75-
List(samDenot.symbol.asTerm.name -> fn.symbol.asTerm),
76-
refinements.toList,
77-
adaptVarargs = true
73+
val implSym = fn.symbol.asTerm
74+
75+
// Check if SAM param erases to array but impl param doesn't (see #23179).
76+
// E.g. SAM expects Array[AnyRef] (erases to Array[Object]) but impl has
77+
// Array[? >: AnyRef] (erases to Object). In this case we need to create
78+
// a wrapper with the SAM signature that casts arguments before forwarding.
79+
val samInfo = samDenot.info
80+
val implInfo = implSym.info
81+
val needsArrayAdaptation = (samInfo, implInfo) match
82+
case (samMt: MethodType, implMt: MethodType) =>
83+
samMt.paramInfos.lazyZip(implMt.paramInfos).exists { (samPt, implPt) =>
84+
erasure(samPt).isInstanceOf[JavaArrayType] && !erasure(implPt).isInstanceOf[JavaArrayType]
85+
}
86+
case _ => false
87+
88+
val forwarderSym =
89+
if needsArrayAdaptation then
90+
// Create a wrapper method with SAM's signature that casts args to impl's types
91+
val wrapperSym = newSymbol(
92+
implSym.owner, implSym.name.asTermName, Synthetic | Method,
93+
samInfo, coord = implSym.span).entered.asTerm
94+
val wrapperDef = DefDef(wrapperSym, paramss => {
95+
val implMt = implInfo.asInstanceOf[MethodType]
96+
val adaptedArgs = paramss.head.lazyZip(implMt.paramInfos).map { (arg, implPt) =>
97+
if arg.tpe =:= implPt then arg else arg.cast(implPt)
98+
}
99+
ref(implSym).appliedToTermArgs(adaptedArgs)
100+
})
101+
cpy.Block(tree)(fn :: wrapperDef :: Nil,
102+
transformFollowingDeep:
103+
AnonClass(List(tpe1),
104+
List(samDenot.symbol.asTerm.name -> wrapperSym),
105+
refinements.toList,
106+
adaptVarargs = true
107+
)
78108
)
79-
)
109+
else
110+
cpy.Block(tree)(stats,
111+
transformFollowingDeep:
112+
AnonClass(List(tpe1),
113+
List(samDenot.symbol.asTerm.name -> implSym),
114+
refinements.toList,
115+
adaptVarargs = true
116+
)
117+
)
118+
forwarderSym
80119
}
81120
case _ =>
82121
tree

0 commit comments

Comments
 (0)