diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index d4e585402feb..f97baa7f7889 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,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 diff --git a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala index ca0f0d7e43bd..33743868b2bc 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala @@ -8,6 +8,7 @@ import Names.{Name, TermName} import Constants.Constant import Names.Name +import StdNames.nme import config.Feature class TypeUtils: @@ -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 diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index b626464bc428..12de7f465f91 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -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)