diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 9a52b51e68cf..d1f5f48ceb87 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -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)) @@ -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 diff --git a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala index 2a926903181a..cbe4a7f8fd9a 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala @@ -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] @@ -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 + diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index c8670cba0ebe..5da6c31d241b 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -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)