diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index d4e585402feb..f5ea9f71c0e4 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -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)) @@ -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 diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 5ab01aa08a1f..ec568c418729 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -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 =>