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.

[Cherry-picked 8aa59f8][modified]
  • Loading branch information
WojciechMazur committed Dec 3, 2024
1 parent f1ec7a5 commit cbbb7e3
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 35 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 @@ -318,21 +318,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 @@ -347,8 +332,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
34 changes: 30 additions & 4 deletions compiler/src/dotty/tools/dotc/core/TypeUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ package core
import TypeErasure.ErasedValueType
import Types.*, Contexts.*, Symbols.*, Flags.*, Decorators.*
import Names.Name
import StdNames.nme

class TypeUtils {
class TypeUtils:
/** A decorator that provides methods on types
* that are needed in the transformer pipeline.
*/
extension (self: Type) {
extension (self: Type)

def isErasedValueType(using Context): Boolean =
self.isInstanceOf[ErasedValueType]
Expand Down Expand Up @@ -125,5 +126,30 @@ class TypeUtils {
def takesImplicitParams(using Context): Boolean = self.stripPoly match
case mt: MethodType => mt.isImplicitMethod || mt.resType.takesImplicitParams
case _ => false
}
}

/** 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 @@ -5612,22 +5612,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 cbbb7e3

Please sign in to comment.