Skip to content

Commit

Permalink
Align SAM test and expansion
Browse files Browse the repository at this point in the history
Fix SAM test to use the same scheme as SAM expansion to determine whether
a type needs zero arguments for construction.
  • Loading branch information
odersky committed Sep 16, 2024
1 parent f0b6763 commit 8aa59f8
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 31 deletions.
22 changes: 5 additions & 17 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -328,21 +328,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
superArgs: List[Tree] = Nil, adaptVarargs: Boolean = false)(using Context): TypeDef =
val firstParent :: otherParents = cls.info.parents: @unchecked

def isApplicable(constr: Symbol): Boolean =
def recur(ctpe: Type): Boolean = ctpe match
case ctpe: PolyType =>
recur(ctpe.instantiate(firstParent.argTypes))
case ctpe: MethodType =>
var paramInfos = ctpe.paramInfos
if adaptVarargs && paramInfos.length == superArgs.length + 1
&& atPhaseNoLater(Phases.elimRepeatedPhase)(constr.info.isVarArgsMethod)
then // accept missing argument for varargs parameter
paramInfos = paramInfos.init
superArgs.corresponds(paramInfos)(_.tpe <:< _)
case _ =>
false
recur(constr.info)

def adaptedSuperArgs(ctpe: Type): List[Tree] = ctpe match
case ctpe: PolyType =>
adaptedSuperArgs(ctpe.instantiate(firstParent.argTypes))
Expand All @@ -357,8 +342,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
val superRef =
if cls.is(Trait) then TypeTree(firstParent)
else
val constr = firstParent.decl(nme.CONSTRUCTOR).suchThat(isApplicable)
New(firstParent, constr.symbol.asTerm, adaptedSuperArgs(constr.info))
val parentConstr = firstParent.applicableConstructors(superArgs.tpes, adaptVarargs) match
case Nil => assert(false, i"no applicable parent constructor of $firstParent for supercall arguments $superArgs")
case constr :: Nil => constr
case _ => assert(false, i"multiple applicable parent constructors of $firstParent for supercall arguments $superArgs")
New(firstParent, parentConstr.asTerm, adaptedSuperArgs(parentConstr.info))

ClassDefWithParents(cls, constr, superRef :: otherParents.map(TypeTree(_)), body)
end ClassDef
Expand Down
26 changes: 26 additions & 0 deletions compiler/src/dotty/tools/dotc/core/TypeUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Names.{Name, TermName}
import Constants.Constant

import Names.Name
import StdNames.nme
import config.Feature

class TypeUtils:
Expand Down Expand Up @@ -189,5 +190,30 @@ class TypeUtils:
def stripRefinement: Type = self match
case self: RefinedOrRecType => self.parent.stripRefinement
case seld => self

/** The constructors of this tyoe that that are applicable to `argTypes`, without needing
* an implicit conversion.
* @param adaptVarargs if true, allow a constructor with just a varargs argument to
* match an empty argument list.
*/
def applicableConstructors(argTypes: List[Type], adaptVarargs: Boolean)(using Context): List[Symbol] =
def isApplicable(constr: Symbol): Boolean =
def recur(ctpe: Type): Boolean = ctpe match
case ctpe: PolyType =>
if argTypes.isEmpty then recur(ctpe.resultType) // no need to know instances
else recur(ctpe.instantiate(self.argTypes))
case ctpe: MethodType =>
var paramInfos = ctpe.paramInfos
if adaptVarargs && paramInfos.length == argTypes.length + 1
&& atPhaseNoLater(Phases.elimRepeatedPhase)(constr.info.isVarArgsMethod)
then // accept missing argument for varargs parameter
paramInfos = paramInfos.init
argTypes.corresponds(paramInfos)(_ <:< _)
case _ =>
false
recur(constr.info)

self.decl(nme.CONSTRUCTOR).altsWith(isApplicable).map(_.symbol)

end TypeUtils

24 changes: 10 additions & 14 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5944,22 +5944,18 @@ object Types extends TypeUtils {

def samClass(tp: Type)(using Context): Symbol = tp match
case tp: ClassInfo =>
def zeroParams(tp: Type): Boolean = tp.stripPoly match
case mt: MethodType =>
val noArgsNeeded = mt.paramInfos match
case Nil => true
case info :: Nil => info.isRepeatedParam
case _ => false
noArgsNeeded && !mt.resultType.isInstanceOf[MethodType]
case et: ExprType => true
case _ => false
def validCtor(cls: Symbol): Boolean =
val ctor = cls.primaryConstructor
(!ctor.exists || zeroParams(ctor.info)) // `ContextFunctionN` does not have constructors
&& (!cls.is(Trait) || validCtor(cls.info.parents.head.classSymbol))
val cls = tp.cls
def takesNoArgs(tp: Type) =
!tp.classSymbol.primaryConstructor.exists
// e.g. `ContextFunctionN` does not have constructors
|| tp.applicableConstructors(Nil, adaptVarargs = true).lengthCompare(1) == 0
// we require a unique constructor so that SAM expansion is deterministic
val noArgsNeeded: Boolean =
takesNoArgs(tp)
&& (!tp.cls.is(Trait) || takesNoArgs(tp.parents.head))
def isInstantiable =
!tp.cls.isOneOf(FinalOrSealed) && (tp.appliedRef <:< tp.selfType)
if validCtor(tp.cls) && isInstantiable then tp.cls
if noArgsNeeded && isInstantiable then tp.cls
else NoSymbol
case tp: AppliedType =>
samClass(tp.superType)
Expand Down

0 comments on commit 8aa59f8

Please sign in to comment.