Skip to content

Commit b0e093a

Browse files
committed
Simplify the identification of param specs and captures.
We don't use `paramSymss` anymore. We now only work with `paramNamess` and `paramInfoss`, which are reliable. We also clean up a bunch of things by making things "more general", requiring less explanation comments. In particular, we make no difference between the outer param and other capture params.
1 parent 7ed9fdd commit b0e093a

File tree

2 files changed

+68
-80
lines changed

2 files changed

+68
-80
lines changed

compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala

Lines changed: 62 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
518518
// If argument is undefined and there is a default getter, call it
519519
val verifiedOrDefault = if (param.hasDefault) {
520520
js.If(js.BinaryOp(js.BinaryOp.===, jsArg, js.Undefined()), {
521-
genCallDefaultGetter(exported.sym, i, param.sym.sourcePos, static) {
521+
genCallDefaultGetter(exported.sym, i, static) {
522522
prevArgsCount => result.take(prevArgsCount).toList.map(_.ref)
523523
}
524524
}, {
@@ -537,7 +537,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
537537
result.toList
538538
}
539539

540-
private def genCallDefaultGetter(sym: Symbol, paramIndex: Int, paramPos: SourcePosition, static: Boolean)(
540+
private def genCallDefaultGetter(sym: Symbol, paramIndex: Int, static: Boolean)(
541541
previousArgsValues: Int => List[js.Tree])(
542542
implicit pos: SourcePosition): js.Tree = {
543543

@@ -562,29 +562,17 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
562562
report.error(
563563
"When overriding a native method with default arguments, " +
564564
"the overriding method must explicitly repeat the default arguments.",
565-
paramPos)
565+
sym.srcPos)
566566
js.Undefined()
567567
}
568568
} else {
569569
genApplyMethod(targetTree, defaultGetter, defaultGetterArgs)
570570
}
571571
}
572572

573-
private def targetSymForDefaultGetter(sym: Symbol): Symbol = {
574-
if (sym.isClassConstructor) {
575-
/*/* Get the companion module class.
576-
* For inner classes the sym.owner.companionModule can be broken,
577-
* therefore companionModule is fetched at uncurryPhase.
578-
*/
579-
val companionModule = enteringPhase(currentRun.namerPhase) {
580-
sym.owner.companionModule
581-
}
582-
companionModule.moduleClass*/
583-
sym.owner.companionModule.moduleClass
584-
} else {
585-
sym.owner
586-
}
587-
}
573+
private def targetSymForDefaultGetter(sym: Symbol): Symbol =
574+
if (sym.isClassConstructor) sym.owner.companionModule.moduleClass
575+
else sym.owner
588576

589577
private def defaultGetterDenot(targetSym: Symbol, sym: Symbol, paramIndex: Int): Denotation =
590578
targetSym.info.member(DefaultGetterName(sym.name.asTermName, paramIndex))
@@ -607,9 +595,8 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
607595
js.This()(encodeClassType(sym.owner))
608596
}
609597

610-
def boxIfNeeded(call: js.Tree): js.Tree = {
598+
def boxIfNeeded(call: js.Tree): js.Tree =
611599
box(call, atPhase(elimErasedValueTypePhase)(sym.info.resultType))
612-
}
613600

614601
if (currentClassSym.isNonNativeJSClass) {
615602
assert(sym.owner == currentClassSym.get, sym.fullName)
@@ -627,19 +614,18 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
627614
private def genThrowTypeError(msg: String = "No matching overload")(implicit pos: Position): js.Tree =
628615
js.Throw(js.JSNew(js.JSGlobalRef("TypeError"), js.StringLiteral(msg) :: Nil))
629616

630-
private final class ParamSpec(val sym: Symbol, val info: Type,
631-
val isRepeated: Boolean, val hasDefault: Boolean) {
617+
private final class ParamSpec(val info: Type, val isRepeated: Boolean, val hasDefault: Boolean) {
632618
override def toString(): String =
633-
s"ParamSpec(${sym.name}, ${info.show}, isRepeated = $isRepeated, hasDefault = $hasDefault)"
619+
i"ParamSpec($info, isRepeated = $isRepeated, hasDefault = $hasDefault)"
634620
}
635621

636622
private object ParamSpec {
637-
def apply(methodSym: Symbol, sym: Symbol, infoAtElimRepeated: Type, infoAtElimEVT: Type,
623+
def apply(methodSym: Symbol, infoAtElimRepeated: Type, infoAtElimEVT: Type,
638624
methodHasDefaultParams: Boolean, paramIndex: Int): ParamSpec = {
639625
val isRepeated = infoAtElimRepeated.isRepeatedParam
640626
val info = if (isRepeated) infoAtElimRepeated.repeatedToSingle else infoAtElimEVT
641627
val hasDefault = methodHasDefaultParams && defaultGetterDenot(methodSym, paramIndex).exists
642-
new ParamSpec(sym, info, isRepeated, hasDefault)
628+
new ParamSpec(info, isRepeated, hasDefault)
643629
}
644630
}
645631

@@ -650,73 +636,70 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
650636

651637
// params: List[ParamSpec] ; captureParams and captureParamsBack: List[js.ParamDef]
652638
val (params, captureParamsFront, captureParamsBack) = {
653-
val paramNamessNow = sym.info.paramNamess
654-
val paramInfosNow = sym.info.paramInfoss.flatten
655-
val paramSymsAtElimRepeated = atPhase(elimRepeatedPhase)(sym.paramSymss.flatten.filter(_.isTerm))
656-
val (paramNamessAtElimRepeated, paramInfosAtElimRepeated, methodHasDefaultParams) =
657-
atPhase(elimRepeatedPhase)((sym.info.paramNamess, sym.info.paramInfoss.flatten, sym.hasDefaultParams))
658-
val (paramNamessAtElimEVT, paramInfosAtElimEVT) =
659-
atPhase(elimErasedValueTypePhase)((sym.info.paramNamess, sym.info.paramInfoss.flatten))
660-
661-
def buildFormalParams(paramSyms: List[Symbol], paramInfosAtElimRepeated: List[Type],
662-
paramInfosAtElimEVT: List[Type]): IndexedSeq[ParamSpec] = {
639+
val (paramNamesAtElimRepeated, paramInfosAtElimRepeated, methodHasDefaultParams) =
640+
atPhase(elimRepeatedPhase)((sym.info.paramNamess.flatten, sym.info.paramInfoss.flatten, sym.hasDefaultParams))
641+
val (paramNamesAtElimEVT, paramInfosAtElimEVT) =
642+
atPhase(elimErasedValueTypePhase)((sym.info.firstParamNames, sym.info.firstParamTypes))
643+
val (paramNamesNow, paramInfosNow) =
644+
(sym.info.firstParamNames, sym.info.firstParamTypes)
645+
646+
val formalParamCount = paramInfosAtElimRepeated.size
647+
648+
def buildFormalParams(formalParamInfosAtElimEVT: List[Type]): IndexedSeq[ParamSpec] = {
663649
(for {
664-
(paramSym, infoAtElimRepeated, infoAtElimEVT, paramIndex) <-
665-
paramSyms.lazyZip(paramInfosAtElimRepeated).lazyZip(paramInfosAtElimEVT).lazyZip(0 until paramSyms.size)
650+
(infoAtElimRepeated, infoAtElimEVT, paramIndex) <-
651+
paramInfosAtElimRepeated.lazyZip(formalParamInfosAtElimEVT).lazyZip(0 until formalParamCount)
666652
} yield {
667-
ParamSpec(sym, paramSym, infoAtElimRepeated, infoAtElimEVT, methodHasDefaultParams, paramIndex)
653+
ParamSpec(sym, infoAtElimRepeated, infoAtElimEVT, methodHasDefaultParams, paramIndex)
668654
}).toIndexedSeq
669655
}
670656

657+
def buildCaptureParams(namesAndInfosNow: List[(TermName, Type)]): List[js.ParamDef] = {
658+
implicit val pos: Position = sym.span
659+
for ((name, info) <- namesAndInfosNow) yield {
660+
js.ParamDef(freshLocalIdent(name.mangledString), NoOriginalName, toIRType(info),
661+
mutable = false, rest = false)
662+
}
663+
}
664+
671665
if (!isConstructorOfNestedJSClass) {
672-
// Easy case: all params are formal params.
673-
assert(paramInfosAtElimRepeated.size == paramInfosAtElimEVT.size,
674-
s"Found ${paramInfosAtElimRepeated.size} params entering elimRepeated but " +
675-
s"${paramInfosAtElimEVT.size} params entering elimErasedValueType for " +
676-
s"non-lifted symbol ${sym.fullName}")
677-
val formalParams = buildFormalParams(paramSymsAtElimRepeated, paramInfosAtElimRepeated, paramInfosAtElimEVT)
666+
// Easy case: all params are formal params
667+
assert(paramInfosAtElimEVT.size == formalParamCount && paramInfosNow.size == formalParamCount,
668+
s"Found $formalParamCount params entering elimRepeated but ${paramInfosAtElimEVT.size} params entering " +
669+
s"elimErasedValueType and ${paramInfosNow.size} params at the back-end for non-lifted symbol ${sym.fullName}")
670+
val formalParams = buildFormalParams(paramInfosAtElimEVT)
678671
(formalParams, Nil, Nil)
672+
} else if (formalParamCount == 0) {
673+
// Fast path: all params are capture params
674+
val captureParams = buildCaptureParams(paramNamesNow.zip(paramInfosNow))
675+
(IndexedSeq.empty, Nil, captureParams)
679676
} else {
680-
/* The `arg$outer` param is added by erasure, following "instructions"
681-
* by explicitouter, while the other capture params are added by
682-
* lambdalift (between elimErasedValueType and now).
683-
*
684-
* Since we cannot reliably get Symbols for parameters created by
685-
* intermediate phases, we have to perform some dance with the
686-
* paramNamess and paramInfoss observed at some phases, combined with
687-
* the paramSymss observed at elimRepeated.
677+
/* Slow path: we have to isolate formal params (which were already present at elimRepeated)
678+
* from capture params (which are later, typically by erasure and/or lambdalift).
688679
*/
689680

690-
val hasOuterParam = {
691-
paramInfosAtElimEVT.size == paramInfosAtElimRepeated.size + 1 &&
692-
paramNamessAtElimEVT.flatten.head == nme.OUTER
681+
def findStartOfFormalParamsIn(paramNames: List[TermName]): Int = {
682+
val start = paramNames.indexOfSlice(paramNamesAtElimRepeated)
683+
assert(start >= 0, s"could not find formal param names $paramNamesAtElimRepeated in $paramNames")
684+
start
693685
}
694-
assert(
695-
hasOuterParam || paramInfosAtElimEVT.size == paramInfosAtElimRepeated.size,
696-
s"Found ${paramInfosAtElimRepeated.size} params entering elimRepeated but " +
697-
s"${paramInfosAtElimEVT.size} params entering elimErasedValueType for " +
698-
s"lifted constructor symbol ${sym.fullName}")
699-
700-
val startOfFormalParams = paramNamessNow.flatten.indexOfSlice(paramNamessAtElimRepeated.flatten)
701-
val formalParamCount = paramInfosAtElimRepeated.size
702-
703-
val nonOuterParamInfosAtElimEVT =
704-
if (hasOuterParam) paramInfosAtElimEVT.tail
705-
else paramInfosAtElimEVT
706-
val formalParams = buildFormalParams(paramSymsAtElimRepeated, paramInfosAtElimRepeated, nonOuterParamInfosAtElimEVT)
707-
708-
val paramNamesAndInfosNow = paramNamessNow.flatten.zip(paramInfosNow)
709-
val (captureParamsFrontNow, restOfParamsNow) = paramNamesAndInfosNow.splitAt(startOfFormalParams)
710-
val captureParamsBackNow = restOfParamsNow.drop(formalParamCount)
711686

712-
def makeCaptureParamDef(nameAndInfo: (TermName, Type)): js.ParamDef = {
713-
implicit val pos: Position = sym.span
714-
js.ParamDef(freshLocalIdent(nameAndInfo._1.mangledString), NoOriginalName, toIRType(nameAndInfo._2),
715-
mutable = false, rest = false)
716-
}
687+
// Find the infos of formal params at elimEVT
688+
val startOfFormalParamsAtElimEVT = findStartOfFormalParamsIn(paramNamesAtElimEVT)
689+
val formalParamInfosAtElimEVT = paramInfosAtElimEVT.drop(startOfFormalParamsAtElimEVT).take(formalParamCount)
690+
691+
// Build the formal param specs from their infos at elimRepeated and elimEVT
692+
val formalParams = buildFormalParams(formalParamInfosAtElimEVT)
693+
694+
// Find the formal params now to isolate the capture params (before and after the formal params)
695+
val startOfFormalParamsNow = findStartOfFormalParamsIn(paramNamesNow)
696+
val paramNamesAndInfosNow = paramNamesNow.zip(paramInfosNow)
697+
val (captureParamsFrontNow, restOfParamsNow) = paramNamesAndInfosNow.splitAt(startOfFormalParamsNow)
698+
val captureParamsBackNow = restOfParamsNow.drop(formalParamCount)
717699

718-
val captureParamsFront = captureParamsFrontNow.map(makeCaptureParamDef(_))
719-
val captureParamsBack = captureParamsBackNow.map(makeCaptureParamDef(_))
700+
// Build the capture param defs from the isolated capture params
701+
val captureParamsFront = buildCaptureParams(captureParamsFrontNow)
702+
val captureParamsBack = buildCaptureParams(captureParamsBackNow)
720703

721704
(formalParams, captureParamsFront, captureParamsBack)
722705
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1481,13 +1481,18 @@ object Types {
14811481
case _ => Nil
14821482
}
14831483

1484-
14851484
/** The parameter types in the first parameter section of a generic type or MethodType, Empty list for others */
14861485
final def firstParamTypes(using Context): List[Type] = stripPoly match {
14871486
case mt: MethodType => mt.paramInfos
14881487
case _ => Nil
14891488
}
14901489

1490+
/** The parameter names in the first parameter section of a generic type or MethodType, Empty list for others */
1491+
final def firstParamNames(using Context): List[TermName] = stripPoly match {
1492+
case mt: MethodType => mt.paramNames
1493+
case _ => Nil
1494+
}
1495+
14911496
/** Is this either not a method at all, or a parameterless method? */
14921497
final def isParameterless(using Context): Boolean = stripPoly match {
14931498
case mt: MethodType => false

0 commit comments

Comments
 (0)