Skip to content

Commit 8aa59f8

Browse files
committed
Align SAM test and expansion
Fix SAM test to use the same scheme as SAM expansion to determine whether a type needs zero arguments for construction.
1 parent f0b6763 commit 8aa59f8

File tree

3 files changed

+41
-31
lines changed

3 files changed

+41
-31
lines changed

compiler/src/dotty/tools/dotc/ast/tpd.scala

+5-17
Original file line numberDiff line numberDiff line change
@@ -328,21 +328,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
328328
superArgs: List[Tree] = Nil, adaptVarargs: Boolean = false)(using Context): TypeDef =
329329
val firstParent :: otherParents = cls.info.parents: @unchecked
330330

331-
def isApplicable(constr: Symbol): Boolean =
332-
def recur(ctpe: Type): Boolean = ctpe match
333-
case ctpe: PolyType =>
334-
recur(ctpe.instantiate(firstParent.argTypes))
335-
case ctpe: MethodType =>
336-
var paramInfos = ctpe.paramInfos
337-
if adaptVarargs && paramInfos.length == superArgs.length + 1
338-
&& atPhaseNoLater(Phases.elimRepeatedPhase)(constr.info.isVarArgsMethod)
339-
then // accept missing argument for varargs parameter
340-
paramInfos = paramInfos.init
341-
superArgs.corresponds(paramInfos)(_.tpe <:< _)
342-
case _ =>
343-
false
344-
recur(constr.info)
345-
346331
def adaptedSuperArgs(ctpe: Type): List[Tree] = ctpe match
347332
case ctpe: PolyType =>
348333
adaptedSuperArgs(ctpe.instantiate(firstParent.argTypes))
@@ -357,8 +342,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
357342
val superRef =
358343
if cls.is(Trait) then TypeTree(firstParent)
359344
else
360-
val constr = firstParent.decl(nme.CONSTRUCTOR).suchThat(isApplicable)
361-
New(firstParent, constr.symbol.asTerm, adaptedSuperArgs(constr.info))
345+
val parentConstr = firstParent.applicableConstructors(superArgs.tpes, adaptVarargs) match
346+
case Nil => assert(false, i"no applicable parent constructor of $firstParent for supercall arguments $superArgs")
347+
case constr :: Nil => constr
348+
case _ => assert(false, i"multiple applicable parent constructors of $firstParent for supercall arguments $superArgs")
349+
New(firstParent, parentConstr.asTerm, adaptedSuperArgs(parentConstr.info))
362350

363351
ClassDefWithParents(cls, constr, superRef :: otherParents.map(TypeTree(_)), body)
364352
end ClassDef

compiler/src/dotty/tools/dotc/core/TypeUtils.scala

+26
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Names.{Name, TermName}
88
import Constants.Constant
99

1010
import Names.Name
11+
import StdNames.nme
1112
import config.Feature
1213

1314
class TypeUtils:
@@ -189,5 +190,30 @@ class TypeUtils:
189190
def stripRefinement: Type = self match
190191
case self: RefinedOrRecType => self.parent.stripRefinement
191192
case seld => self
193+
194+
/** The constructors of this tyoe that that are applicable to `argTypes`, without needing
195+
* an implicit conversion.
196+
* @param adaptVarargs if true, allow a constructor with just a varargs argument to
197+
* match an empty argument list.
198+
*/
199+
def applicableConstructors(argTypes: List[Type], adaptVarargs: Boolean)(using Context): List[Symbol] =
200+
def isApplicable(constr: Symbol): Boolean =
201+
def recur(ctpe: Type): Boolean = ctpe match
202+
case ctpe: PolyType =>
203+
if argTypes.isEmpty then recur(ctpe.resultType) // no need to know instances
204+
else recur(ctpe.instantiate(self.argTypes))
205+
case ctpe: MethodType =>
206+
var paramInfos = ctpe.paramInfos
207+
if adaptVarargs && paramInfos.length == argTypes.length + 1
208+
&& atPhaseNoLater(Phases.elimRepeatedPhase)(constr.info.isVarArgsMethod)
209+
then // accept missing argument for varargs parameter
210+
paramInfos = paramInfos.init
211+
argTypes.corresponds(paramInfos)(_ <:< _)
212+
case _ =>
213+
false
214+
recur(constr.info)
215+
216+
self.decl(nme.CONSTRUCTOR).altsWith(isApplicable).map(_.symbol)
217+
192218
end TypeUtils
193219

compiler/src/dotty/tools/dotc/core/Types.scala

+10-14
Original file line numberDiff line numberDiff line change
@@ -5944,22 +5944,18 @@ object Types extends TypeUtils {
59445944

59455945
def samClass(tp: Type)(using Context): Symbol = tp match
59465946
case tp: ClassInfo =>
5947-
def zeroParams(tp: Type): Boolean = tp.stripPoly match
5948-
case mt: MethodType =>
5949-
val noArgsNeeded = mt.paramInfos match
5950-
case Nil => true
5951-
case info :: Nil => info.isRepeatedParam
5952-
case _ => false
5953-
noArgsNeeded && !mt.resultType.isInstanceOf[MethodType]
5954-
case et: ExprType => true
5955-
case _ => false
5956-
def validCtor(cls: Symbol): Boolean =
5957-
val ctor = cls.primaryConstructor
5958-
(!ctor.exists || zeroParams(ctor.info)) // `ContextFunctionN` does not have constructors
5959-
&& (!cls.is(Trait) || validCtor(cls.info.parents.head.classSymbol))
5947+
val cls = tp.cls
5948+
def takesNoArgs(tp: Type) =
5949+
!tp.classSymbol.primaryConstructor.exists
5950+
// e.g. `ContextFunctionN` does not have constructors
5951+
|| tp.applicableConstructors(Nil, adaptVarargs = true).lengthCompare(1) == 0
5952+
// we require a unique constructor so that SAM expansion is deterministic
5953+
val noArgsNeeded: Boolean =
5954+
takesNoArgs(tp)
5955+
&& (!tp.cls.is(Trait) || takesNoArgs(tp.parents.head))
59605956
def isInstantiable =
59615957
!tp.cls.isOneOf(FinalOrSealed) && (tp.appliedRef <:< tp.selfType)
5962-
if validCtor(tp.cls) && isInstantiable then tp.cls
5958+
if noArgsNeeded && isInstantiable then tp.cls
59635959
else NoSymbol
59645960
case tp: AppliedType =>
59655961
samClass(tp.superType)

0 commit comments

Comments
 (0)