Skip to content

Commit

Permalink
Align SAM test and SAM expansion
Browse files Browse the repository at this point in the history
  • Loading branch information
odersky committed Sep 16, 2024
1 parent 4696911 commit 25996c7
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 51 deletions.
26 changes: 9 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,15 @@ 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))
var parentConstrs = firstParent.applicableConstructors(superArgs.tpes, adaptVarargs)
assert(parentConstrs.nonEmpty, i"no applicable parent constructor of $firstParent for supercall arguments $superArgs")
if parentConstrs.tail.nonEmpty then
// try prioritizing constructors that don't take additional varargs
val parentConstrs1 = firstParent.applicableConstructors(superArgs.tpes, adaptVarargs = false)
assert(parentConstrs1.length == 1, i"multiple applicable parent constructors of $firstParent for supercall arguments $superArgs")
parentConstrs = parentConstrs1
val parentConstr = parentConstrs.head
New(firstParent, parentConstr.asTerm, adaptedSuperArgs(parentConstr.info))

ClassDefWithParents(cls, constr, superRef :: otherParents.map(TypeTree(_)), body)
end ClassDef
Expand Down
49 changes: 15 additions & 34 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5942,41 +5942,22 @@ object Types extends TypeUtils {
approxWildcardArgs(tp)
end samParent

def isSamClass(tp: Type, cls: ClassSymbol)(using Context): Boolean =
def takesNoArgs(tp: Type) =
!cls.primaryConstructor.exists // `ContextFunctionN` does not have constructors
|| tp.applicableConstructors(Nil, adaptVarargs = true).nonEmpty
val noArgsNeeded: Boolean =
takesNoArgs(tp) && (!cls.is(Trait) || takesNoArgs(tp.parents.head))
def isInstantiable =
val cinfo = cls.classInfo
!cls.isOneOf(FinalOrSealed) && (cinfo.appliedRef <:< cinfo.selfType)
noArgsNeeded && isInstantiable

def samClass(tp: Type)(using Context): Symbol = tp match
case tp: ClassInfo =>
val cls = tp.cls
def zeroParamsOLD(tp: Type): Boolean = tp.stripPoly match
case mt: MethodType => mt.paramInfos.isEmpty && !mt.resultType.isInstanceOf[MethodType]
case et: ExprType => true
case _ => false
val validCtorOLD =
val ctor = cls.primaryConstructor
// `ContextFunctionN` does not have constructors
!ctor.exists || zeroParamsOLD(ctor.info)

def takesNoArgs(tp: Type) =
!tp.classSymbol.primaryConstructor.exists // `ContextFunctionN` does not have constructors
|| tp.applicableConstructors(Nil, adaptVarargs = true).nonEmpty
def firstParentCls = tp.parents.head.classSymbol
val noArgsNeeded: Boolean =
takesNoArgs(tp)
&& (!tp.cls.is(Trait) || takesNoArgs(tp.parents.head))

if noArgsNeeded != validCtorOLD then
println(
i"""SAM change for $tp with parent ${firstParentCls.fullName}, now $noArgsNeeded
|takesNoArgs: ${takesNoArgs(tp)}
|takesNoArgsParent: ${takesNoArgs(tp.cls.info.parents.head)}
|primary: ${firstParentCls.primaryConstructor.info}""")

def isInstantiable =
!tp.cls.isOneOf(FinalOrSealed) && (tp.appliedRef <:< tp.selfType)
if noArgsNeeded && isInstantiable then tp.cls
else NoSymbol
case tp: AppliedType =>
samClass(tp.superType)
case tp: TypeRef =>
samClass(tp.underlying)
case tp: (AppliedType | TypeRef) =>
tp.typeSymbol match
case cls: ClassSymbol => if isSamClass(tp, cls) then cls else NoSymbol
case _ => samClass(tp.superType)
case tp: RefinedType =>
samClass(tp.underlying)
case tp: TypeBounds =>
Expand Down

0 comments on commit 25996c7

Please sign in to comment.