Skip to content

Commit cbbb7e3

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. [Cherry-picked 8aa59f8][modified]
1 parent f1ec7a5 commit cbbb7e3

File tree

3 files changed

+45
-35
lines changed

3 files changed

+45
-35
lines changed

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

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -318,21 +318,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
318318
superArgs: List[Tree] = Nil, adaptVarargs: Boolean = false)(using Context): TypeDef =
319319
val firstParent :: otherParents = cls.info.parents: @unchecked
320320

321-
def isApplicable(constr: Symbol): Boolean =
322-
def recur(ctpe: Type): Boolean = ctpe match
323-
case ctpe: PolyType =>
324-
recur(ctpe.instantiate(firstParent.argTypes))
325-
case ctpe: MethodType =>
326-
var paramInfos = ctpe.paramInfos
327-
if adaptVarargs && paramInfos.length == superArgs.length + 1
328-
&& atPhaseNoLater(Phases.elimRepeatedPhase)(constr.info.isVarArgsMethod)
329-
then // accept missing argument for varargs parameter
330-
paramInfos = paramInfos.init
331-
superArgs.corresponds(paramInfos)(_.tpe <:< _)
332-
case _ =>
333-
false
334-
recur(constr.info)
335-
336321
def adaptedSuperArgs(ctpe: Type): List[Tree] = ctpe match
337322
case ctpe: PolyType =>
338323
adaptedSuperArgs(ctpe.instantiate(firstParent.argTypes))
@@ -347,8 +332,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
347332
val superRef =
348333
if cls.is(Trait) then TypeTree(firstParent)
349334
else
350-
val constr = firstParent.decl(nme.CONSTRUCTOR).suchThat(isApplicable)
351-
New(firstParent, constr.symbol.asTerm, adaptedSuperArgs(constr.info))
335+
val parentConstr = firstParent.applicableConstructors(superArgs.tpes, adaptVarargs) match
336+
case Nil => assert(false, i"no applicable parent constructor of $firstParent for supercall arguments $superArgs")
337+
case constr :: Nil => constr
338+
case _ => assert(false, i"multiple applicable parent constructors of $firstParent for supercall arguments $superArgs")
339+
New(firstParent, parentConstr.asTerm, adaptedSuperArgs(parentConstr.info))
352340

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

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

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ package core
55
import TypeErasure.ErasedValueType
66
import Types.*, Contexts.*, Symbols.*, Flags.*, Decorators.*
77
import Names.Name
8+
import StdNames.nme
89

9-
class TypeUtils {
10+
class TypeUtils:
1011
/** A decorator that provides methods on types
1112
* that are needed in the transformer pipeline.
1213
*/
13-
extension (self: Type) {
14+
extension (self: Type)
1415

1516
def isErasedValueType(using Context): Boolean =
1617
self.isInstanceOf[ErasedValueType]
@@ -125,5 +126,30 @@ class TypeUtils {
125126
def takesImplicitParams(using Context): Boolean = self.stripPoly match
126127
case mt: MethodType => mt.isImplicitMethod || mt.resType.takesImplicitParams
127128
case _ => false
128-
}
129-
}
129+
130+
/** The constructors of this tyoe that that are applicable to `argTypes`, without needing
131+
* an implicit conversion.
132+
* @param adaptVarargs if true, allow a constructor with just a varargs argument to
133+
* match an empty argument list.
134+
*/
135+
def applicableConstructors(argTypes: List[Type], adaptVarargs: Boolean)(using Context): List[Symbol] =
136+
def isApplicable(constr: Symbol): Boolean =
137+
def recur(ctpe: Type): Boolean = ctpe match
138+
case ctpe: PolyType =>
139+
if argTypes.isEmpty then recur(ctpe.resultType) // no need to know instances
140+
else recur(ctpe.instantiate(self.argTypes))
141+
case ctpe: MethodType =>
142+
var paramInfos = ctpe.paramInfos
143+
if adaptVarargs && paramInfos.length == argTypes.length + 1
144+
&& atPhaseNoLater(Phases.elimRepeatedPhase)(constr.info.isVarArgsMethod)
145+
then // accept missing argument for varargs parameter
146+
paramInfos = paramInfos.init
147+
argTypes.corresponds(paramInfos)(_ <:< _)
148+
case _ =>
149+
false
150+
recur(constr.info)
151+
152+
self.decl(nme.CONSTRUCTOR).altsWith(isApplicable).map(_.symbol)
153+
154+
end TypeUtils
155+

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

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5612,22 +5612,18 @@ object Types extends TypeUtils {
56125612

56135613
def samClass(tp: Type)(using Context): Symbol = tp match
56145614
case tp: ClassInfo =>
5615-
def zeroParams(tp: Type): Boolean = tp.stripPoly match
5616-
case mt: MethodType =>
5617-
val noArgsNeeded = mt.paramInfos match
5618-
case Nil => true
5619-
case info :: Nil => info.isRepeatedParam
5620-
case _ => false
5621-
noArgsNeeded && !mt.resultType.isInstanceOf[MethodType]
5622-
case et: ExprType => true
5623-
case _ => false
5624-
def validCtor(cls: Symbol): Boolean =
5625-
val ctor = cls.primaryConstructor
5626-
(!ctor.exists || zeroParams(ctor.info)) // `ContextFunctionN` does not have constructors
5627-
&& (!cls.is(Trait) || validCtor(cls.info.parents.head.classSymbol))
5615+
val cls = tp.cls
5616+
def takesNoArgs(tp: Type) =
5617+
!tp.classSymbol.primaryConstructor.exists
5618+
// e.g. `ContextFunctionN` does not have constructors
5619+
|| tp.applicableConstructors(Nil, adaptVarargs = true).lengthCompare(1) == 0
5620+
// we require a unique constructor so that SAM expansion is deterministic
5621+
val noArgsNeeded: Boolean =
5622+
takesNoArgs(tp)
5623+
&& (!tp.cls.is(Trait) || takesNoArgs(tp.parents.head))
56285624
def isInstantiable =
56295625
!tp.cls.isOneOf(FinalOrSealed) && (tp.appliedRef <:< tp.selfType)
5630-
if validCtor(tp.cls) && isInstantiable then tp.cls
5626+
if noArgsNeeded && isInstantiable then tp.cls
56315627
else NoSymbol
56325628
case tp: AppliedType =>
56335629
samClass(tp.superType)

0 commit comments

Comments
 (0)