Skip to content

Commit 125cc18

Browse files
oderskyWojciechMazur
authored andcommitted
Three fixes for SAM type handling
The first two fixes concern characterization of SAM types. One condition of a SAM type is that it can be instantiated with an empty argument list. This was implemented incorrectly. First, we missed the case where the SAM type is a trait with a parent class that takes arguments. In this case the SAM type _cannot_ be instantiated with an empty argument list. Second, we missed the case where the SAM type constructor has a single vararg parameter. In this case the SAM type _can_ be instantiated with an empty argument list. The second case was also translated incorrectly which led to illegal bytecodes. Fixes #15855 [Cherry-picked f0b6763]
1 parent a8f2409 commit 125cc18

File tree

6 files changed

+105
-31
lines changed

6 files changed

+105
-31
lines changed

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

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -305,24 +305,53 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
305305
def TypeDef(sym: TypeSymbol)(using Context): TypeDef =
306306
ta.assignType(untpd.TypeDef(sym.name, TypeTree(sym.info)), sym)
307307

308-
def ClassDef(cls: ClassSymbol, constr: DefDef, body: List[Tree], superArgs: List[Tree] = Nil)(using Context): TypeDef = {
308+
/** Create a class definition
309+
* @param cls the class symbol of the created class
310+
* @param constr its primary constructor
311+
* @param body the statements in its template
312+
* @param superArgs the arguments to pass to the superclass constructor
313+
* @param adaptVarargs if true, allow matching a vararg superclass constructor
314+
* with a missing argument in superArgs, and synthesize an
315+
* empty repeated parameter in the supercall in this case
316+
*/
317+
def ClassDef(cls: ClassSymbol, constr: DefDef, body: List[Tree],
318+
superArgs: List[Tree] = Nil, adaptVarargs: Boolean = false)(using Context): TypeDef =
309319
val firstParent :: otherParents = cls.info.parents: @unchecked
320+
321+
def isApplicable(constr: Symbol): Boolean =
322+
def recur(ctpe: Type): Boolean = ctpe match
323+
case ctpe: PolyType =>
324+
recur(ctpe.instantiate(firstParent.argTypes))
325+
case ctpe: MethodType =>
326+
var paramInfos = ctpe.paramInfos
327+
if adaptVarargs && paramInfos.length == superArgs.length + 1
328+
&& atPhaseNoLater(Phases.elimRepeatedPhase)(constr.info.isVarArgsMethod)
329+
then // accept missing argument for varargs parameter
330+
paramInfos = paramInfos.init
331+
superArgs.corresponds(paramInfos)(_.tpe <:< _)
332+
case _ =>
333+
false
334+
recur(constr.info)
335+
336+
def adaptedSuperArgs(ctpe: Type): List[Tree] = ctpe match
337+
case ctpe: PolyType =>
338+
adaptedSuperArgs(ctpe.instantiate(firstParent.argTypes))
339+
case ctpe: MethodType
340+
if ctpe.paramInfos.length == superArgs.length + 1 =>
341+
// last argument must be a vararg, otherwise isApplicable would have failed
342+
superArgs :+
343+
repeated(Nil, TypeTree(ctpe.paramInfos.last.argInfos.head, inferred = true))
344+
case _ =>
345+
superArgs
346+
310347
val superRef =
311-
if (cls.is(Trait)) TypeTree(firstParent)
312-
else {
313-
def isApplicable(ctpe: Type): Boolean = ctpe match {
314-
case ctpe: PolyType =>
315-
isApplicable(ctpe.instantiate(firstParent.argTypes))
316-
case ctpe: MethodType =>
317-
(superArgs corresponds ctpe.paramInfos)(_.tpe <:< _)
318-
case _ =>
319-
false
320-
}
321-
val constr = firstParent.decl(nme.CONSTRUCTOR).suchThat(constr => isApplicable(constr.info))
322-
New(firstParent, constr.symbol.asTerm, superArgs)
323-
}
348+
if cls.is(Trait) then TypeTree(firstParent)
349+
else
350+
val constr = firstParent.decl(nme.CONSTRUCTOR).suchThat(isApplicable)
351+
New(firstParent, constr.symbol.asTerm, adaptedSuperArgs(constr.info))
352+
324353
ClassDefWithParents(cls, constr, superRef :: otherParents.map(TypeTree(_)), body)
325-
}
354+
end ClassDef
326355

327356
def ClassDefWithParents(cls: ClassSymbol, constr: DefDef, parents: List[Tree], body: List[Tree])(using Context): TypeDef = {
328357
val selfType =
@@ -349,13 +378,18 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
349378
* @param parents a non-empty list of class types
350379
* @param termForwarders a non-empty list of forwarding definitions specified by their name and the definition they forward to.
351380
* @param typeMembers a possibly-empty list of type members specified by their name and their right hand side.
381+
* @param adaptVarargs if true, allow matching a vararg superclass constructor
382+
* with a missing argument in superArgs, and synthesize an
383+
* empty repeated parameter in the supercall in this case
352384
*
353385
* The class has the same owner as the first function in `termForwarders`.
354386
* Its position is the union of all symbols in `termForwarders`.
355387
*/
356-
def AnonClass(parents: List[Type], termForwarders: List[(TermName, TermSymbol)],
357-
typeMembers: List[(TypeName, TypeBounds)] = Nil)(using Context): Block = {
358-
AnonClass(termForwarders.head._2.owner, parents, termForwarders.map(_._2.span).reduceLeft(_ union _)) { cls =>
388+
def AnonClass(parents: List[Type],
389+
termForwarders: List[(TermName, TermSymbol)],
390+
typeMembers: List[(TypeName, TypeBounds)],
391+
adaptVarargs: Boolean)(using Context): Block = {
392+
AnonClass(termForwarders.head._2.owner, parents, termForwarders.map(_._2.span).reduceLeft(_ union _), adaptVarargs) { cls =>
359393
def forwarder(name: TermName, fn: TermSymbol) = {
360394
val fwdMeth = fn.copy(cls, name, Synthetic | Method | Final).entered.asTerm
361395
for overridden <- fwdMeth.allOverriddenSymbols do
@@ -375,6 +409,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
375409
* with the specified owner and position.
376410
*/
377411
def AnonClass(owner: Symbol, parents: List[Type], coord: Coord)(body: ClassSymbol => List[Tree])(using Context): Block =
412+
AnonClass(owner, parents, coord, adaptVarargs = false)(body)
413+
414+
private def AnonClass(owner: Symbol, parents: List[Type], coord: Coord, adaptVarargs: Boolean)(body: ClassSymbol => List[Tree])(using Context): Block =
378415
val parents1 =
379416
if (parents.head.classSymbol.is(Trait)) {
380417
val head = parents.head.parents.head
@@ -383,7 +420,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
383420
else parents
384421
val cls = newNormalizedClassSymbol(owner, tpnme.ANON_CLASS, Synthetic | Final, parents1, coord = coord)
385422
val constr = newConstructor(cls, Synthetic, Nil, Nil).entered
386-
val cdef = ClassDef(cls, DefDef(constr), body(cls))
423+
val cdef = ClassDef(cls, DefDef(constr), body(cls), Nil, adaptVarargs)
387424
Block(cdef :: Nil, New(cls.typeRef, Nil))
388425

389426
def Import(expr: Tree, selectors: List[untpd.ImportSelector])(using Context): Import =

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ object Phases {
486486
def sbtExtractDependenciesPhase(using Context): Phase = ctx.base.sbtExtractDependenciesPhase
487487
def picklerPhase(using Context): Phase = ctx.base.picklerPhase
488488
def inliningPhase(using Context): Phase = ctx.base.inliningPhase
489-
def stagingPhase(using Context): Phase = ctx.base.stagingPhase
489+
def stagingPhase(using Context): Phase = ctx.base.stagingPhase
490490
def splicingPhase(using Context): Phase = ctx.base.splicingPhase
491491
def firstTransformPhase(using Context): Phase = ctx.base.firstTransformPhase
492492
def refchecksPhase(using Context): Phase = ctx.base.refchecksPhase

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

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5613,16 +5613,21 @@ object Types extends TypeUtils {
56135613
def samClass(tp: Type)(using Context): Symbol = tp match
56145614
case tp: ClassInfo =>
56155615
def zeroParams(tp: Type): Boolean = tp.stripPoly match
5616-
case mt: MethodType => mt.paramInfos.isEmpty && !mt.resultType.isInstanceOf[MethodType]
5616+
case mt: MethodType =>
5617+
val noArgsNeeded = mt.paramInfos match
5618+
case Nil => true
5619+
case info :: Nil => info.isRepeatedParam
5620+
case _ => false
5621+
noArgsNeeded && !mt.resultType.isInstanceOf[MethodType]
56175622
case et: ExprType => true
56185623
case _ => false
5619-
val cls = tp.cls
5620-
val validCtor =
5624+
def validCtor(cls: Symbol): Boolean =
56215625
val ctor = cls.primaryConstructor
5622-
// `ContextFunctionN` does not have constructors
5623-
!ctor.exists || zeroParams(ctor.info)
5624-
val isInstantiable = !cls.isOneOf(FinalOrSealed) && (tp.appliedRef <:< tp.selfType)
5625-
if validCtor && isInstantiable then tp.cls
5626+
(!ctor.exists || zeroParams(ctor.info)) // `ContextFunctionN` does not have constructors
5627+
&& (!cls.is(Trait) || validCtor(cls.info.parents.head.classSymbol))
5628+
def isInstantiable =
5629+
!tp.cls.isOneOf(FinalOrSealed) && (tp.appliedRef <:< tp.selfType)
5630+
if validCtor(tp.cls) && isInstantiable then tp.cls
56265631
else NoSymbol
56275632
case tp: AppliedType =>
56285633
samClass(tp.superType)

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,12 @@ class ExpandSAMs extends MiniPhase:
6969
val tpe1 = collectAndStripRefinements(tpe)
7070
val Seq(samDenot) = tpe1.possibleSamMethods
7171
cpy.Block(tree)(stats,
72-
AnonClass(List(tpe1),
73-
List(samDenot.symbol.asTerm.name -> fn.symbol.asTerm),
74-
refinements.toList
75-
)
72+
transformFollowingDeep:
73+
AnonClass(List(tpe1),
74+
List(samDenot.symbol.asTerm.name -> fn.symbol.asTerm),
75+
refinements.toList,
76+
adaptVarargs = true
77+
)
7678
)
7779
}
7880
case _ =>

tests/neg/i15855.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// crash.scala
2+
import scala.language.implicitConversions
3+
4+
class MyFunction(args: String)
5+
6+
trait MyFunction0[+R] extends MyFunction {
7+
def apply(): R
8+
}
9+
10+
def fromFunction0[R](f: Function0[R]): MyFunction0[R] = () => f() // error

tests/run/i15855.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// crash.scala
2+
import scala.language.implicitConversions
3+
4+
class MyFunction(args: String*)
5+
6+
trait MyFunction0[+R] extends MyFunction {
7+
def apply(): R
8+
}
9+
10+
abstract class MyFunction1[R](args: R*):
11+
def apply(): R
12+
13+
def fromFunction0[R](f: Function0[R]): MyFunction0[R] = () => f()
14+
def fromFunction1[R](f: Function0[R]): MyFunction1[R] = () => f()
15+
16+
@main def Test =
17+
val m0: MyFunction0[Int] = fromFunction0(() => 1)
18+
val m1: MyFunction1[Int] = fromFunction1(() => 2)
19+
assert(m0() == 1)
20+
assert(m1() == 2)

0 commit comments

Comments
 (0)