Skip to content

Commit 2666e70

Browse files
committed
TypeErasure#apply: replace ensuring by inlinable assert for performance
1 parent 7de3663 commit 2666e70

File tree

1 file changed

+123
-121
lines changed

1 file changed

+123
-121
lines changed

Diff for: compiler/src/dotty/tools/dotc/core/TypeErasure.scala

+123-121
Original file line numberDiff line numberDiff line change
@@ -631,129 +631,131 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
631631
* - For NoType or NoPrefix, the type itself.
632632
* - For any other type, exception.
633633
*/
634-
private def apply(tp: Type)(using Context): Type | Null = (tp match
635-
case _: ErasedValueType =>
636-
tp
637-
case tp: TypeRef =>
638-
val sym = tp.symbol
639-
if !sym.isClass then this(checkedSuperType(tp))
640-
else if semiEraseVCs && isDerivedValueClass(sym) then eraseDerivedValueClass(tp)
641-
else if defn.isSyntheticFunctionClass(sym) then defn.functionTypeErasure(sym)
642-
else eraseNormalClassRef(tp)
643-
case tp: AppliedType =>
644-
val tycon = tp.tycon
645-
if (tycon.isRef(defn.ArrayClass)) eraseArray(tp)
646-
else if (tycon.isRef(defn.PairClass)) erasePair(tp)
647-
else if (tp.isRepeatedParam) apply(tp.translateFromRepeated(toArray = sourceLanguage.isJava))
648-
else if (semiEraseVCs && isDerivedValueClass(tycon.classSymbol)) eraseDerivedValueClass(tp)
649-
else this(checkedSuperType(tp))
650-
case tp: TermRef =>
651-
this(underlyingOfTermRef(tp))
652-
case _: ThisType =>
653-
this(tp.widen)
654-
case SuperType(thistpe, supertpe) =>
655-
val eThis = this(thistpe)
656-
val eSuper = this(supertpe)
657-
if eThis == null || eSuper == null then null
658-
else SuperType(eThis, eSuper)
659-
case ExprType(rt) =>
660-
defn.FunctionType(0)
661-
case RefinedType(parent, nme.apply, refinedInfo) if parent.typeSymbol eq defn.PolyFunctionClass =>
662-
erasePolyFunctionApply(refinedInfo)
663-
case RefinedType(parent, nme.apply, refinedInfo: MethodType) if defn.isErasedFunctionType(parent) =>
664-
eraseErasedFunctionApply(refinedInfo)
665-
case tp: TypeVar if !tp.isInstantiated =>
666-
assert(inSigName, i"Cannot erase uninstantiated type variable $tp")
667-
null
668-
case tp: TypeProxy =>
669-
this(tp.underlying)
670-
case tp @ AndType(tp1, tp2) =>
671-
if sourceLanguage.isJava then
672-
this(tp1)
673-
else if sourceLanguage.isScala2 then
674-
this(Scala2Erasure.intersectionDominator(Scala2Erasure.flattenedParents(tp)))
675-
else
676-
val e1 = this(tp1)
677-
val e2 = this(tp2)
678-
if e1 == null || e2 == null then null
679-
else erasedGlb(e1, e2)
680-
case OrType(tp1, tp2) =>
681-
if isSymbol && sourceLanguage.isScala2 && ctx.settings.scalajs.value then
682-
// In Scala2Unpickler we unpickle Scala.js pseudo-unions as if they were
683-
// real unions, but we must still erase them as Scala 2 would to emit
684-
// the correct signatures in SJSIR.
685-
// We only do this when `isSymbol` is true since in other situations we
686-
// cannot distinguish a Scala.js pseudo-union from a Scala 3 union that
687-
// has been substituted into a Scala 2 type (e.g., via `asSeenFrom`),
688-
// erasing these unions as if they were pseudo-unions could have an
689-
// impact on overriding relationships so it's best to leave them
690-
// alone (and this doesn't impact the SJSIR we generate).
691-
JSDefinitions.jsdefn.PseudoUnionType
692-
else
693-
val e1 = this(tp1)
694-
val e2 = this(tp2)
695-
if e1 == null || e2 == null then null
696-
else TypeComparer.orType(e1, e2, isErased = true)
697-
case tp: MethodType =>
698-
def paramErasure(tpToErase: Type) =
699-
erasureFn(sourceLanguage, semiEraseVCs, isConstructor, isSymbol, inSigName = false)(tpToErase).nn
700-
val (names, formals0) = if tp.hasErasedParams then
701-
tp.paramNames
702-
.zip(tp.paramInfos)
703-
.zip(tp.erasedParams)
704-
.collect{ case (param, isErased) if !isErased => param }
705-
.unzip
706-
else (tp.paramNames, tp.paramInfos)
707-
val formals = formals0.mapConserve(paramErasure)
708-
eraseResult(tp.resultType) match {
709-
case rt: MethodType =>
710-
tp.derivedLambdaType(names ++ rt.paramNames, formals ++ rt.paramInfos, rt.resultType)
711-
case NoType =>
712-
// Can happen if we smuggle in a Nothing in the qualifier. Normally we prevent that
713-
// in Checking.checkMembersOK, but compiler-generated code can bypass this test.
714-
// See i15377.scala for a test case.
715-
NoType
716-
case rt =>
717-
tp.derivedLambdaType(names, formals, rt)
718-
}
719-
case tp: PolyType =>
720-
this(tp.resultType)
721-
case tp @ ClassInfo(pre, cls, parents, decls, _) =>
722-
if (cls.is(Package)) tp
723-
else {
724-
def eraseParent(tp: Type) = tp.dealias match { // note: can't be opaque, since it's a class parent
725-
case tp: AppliedType if tp.tycon.isRef(defn.PairClass) => defn.ObjectType
726-
case _ => apply(tp).nn
634+
private def apply(tp: Type)(using Context): Type | Null =
635+
val etp = tp match
636+
case _: ErasedValueType =>
637+
tp
638+
case tp: TypeRef =>
639+
val sym = tp.symbol
640+
if !sym.isClass then this(checkedSuperType(tp))
641+
else if semiEraseVCs && isDerivedValueClass(sym) then eraseDerivedValueClass(tp)
642+
else if defn.isSyntheticFunctionClass(sym) then defn.functionTypeErasure(sym)
643+
else eraseNormalClassRef(tp)
644+
case tp: AppliedType =>
645+
val tycon = tp.tycon
646+
if (tycon.isRef(defn.ArrayClass)) eraseArray(tp)
647+
else if (tycon.isRef(defn.PairClass)) erasePair(tp)
648+
else if (tp.isRepeatedParam) apply(tp.translateFromRepeated(toArray = sourceLanguage.isJava))
649+
else if (semiEraseVCs && isDerivedValueClass(tycon.classSymbol)) eraseDerivedValueClass(tp)
650+
else this(checkedSuperType(tp))
651+
case tp: TermRef =>
652+
this(underlyingOfTermRef(tp))
653+
case _: ThisType =>
654+
this(tp.widen)
655+
case SuperType(thistpe, supertpe) =>
656+
val eThis = this(thistpe)
657+
val eSuper = this(supertpe)
658+
if eThis == null || eSuper == null then null
659+
else SuperType(eThis, eSuper)
660+
case ExprType(rt) =>
661+
defn.FunctionType(0)
662+
case RefinedType(parent, nme.apply, refinedInfo) if parent.typeSymbol eq defn.PolyFunctionClass =>
663+
erasePolyFunctionApply(refinedInfo)
664+
case RefinedType(parent, nme.apply, refinedInfo: MethodType) if defn.isErasedFunctionType(parent) =>
665+
eraseErasedFunctionApply(refinedInfo)
666+
case tp: TypeVar if !tp.isInstantiated =>
667+
assert(inSigName, i"Cannot erase uninstantiated type variable $tp")
668+
null
669+
case tp: TypeProxy =>
670+
this(tp.underlying)
671+
case tp @ AndType(tp1, tp2) =>
672+
if sourceLanguage.isJava then
673+
this(tp1)
674+
else if sourceLanguage.isScala2 then
675+
this(Scala2Erasure.intersectionDominator(Scala2Erasure.flattenedParents(tp)))
676+
else
677+
val e1 = this(tp1)
678+
val e2 = this(tp2)
679+
if e1 == null || e2 == null then null
680+
else erasedGlb(e1, e2)
681+
case OrType(tp1, tp2) =>
682+
if isSymbol && sourceLanguage.isScala2 && ctx.settings.scalajs.value then
683+
// In Scala2Unpickler we unpickle Scala.js pseudo-unions as if they were
684+
// real unions, but we must still erase them as Scala 2 would to emit
685+
// the correct signatures in SJSIR.
686+
// We only do this when `isSymbol` is true since in other situations we
687+
// cannot distinguish a Scala.js pseudo-union from a Scala 3 union that
688+
// has been substituted into a Scala 2 type (e.g., via `asSeenFrom`),
689+
// erasing these unions as if they were pseudo-unions could have an
690+
// impact on overriding relationships so it's best to leave them
691+
// alone (and this doesn't impact the SJSIR we generate).
692+
JSDefinitions.jsdefn.PseudoUnionType
693+
else
694+
val e1 = this(tp1)
695+
val e2 = this(tp2)
696+
if e1 == null || e2 == null then null
697+
else TypeComparer.orType(e1, e2, isErased = true)
698+
case tp: MethodType =>
699+
def paramErasure(tpToErase: Type) =
700+
erasureFn(sourceLanguage, semiEraseVCs, isConstructor, isSymbol, inSigName = false)(tpToErase).nn
701+
val (names, formals0) = if tp.hasErasedParams then
702+
tp.paramNames
703+
.zip(tp.paramInfos)
704+
.zip(tp.erasedParams)
705+
.collect{ case (param, isErased) if !isErased => param }
706+
.unzip
707+
else (tp.paramNames, tp.paramInfos)
708+
val formals = formals0.mapConserve(paramErasure)
709+
eraseResult(tp.resultType) match {
710+
case rt: MethodType =>
711+
tp.derivedLambdaType(names ++ rt.paramNames, formals ++ rt.paramInfos, rt.resultType)
712+
case NoType =>
713+
// Can happen if we smuggle in a Nothing in the qualifier. Normally we prevent that
714+
// in Checking.checkMembersOK, but compiler-generated code can bypass this test.
715+
// See i15377.scala for a test case.
716+
NoType
717+
case rt =>
718+
tp.derivedLambdaType(names, formals, rt)
727719
}
728-
val erasedParents: List[Type] =
729-
if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil
730-
else parents.mapConserve(eraseParent) match {
731-
case tr :: trs1 =>
732-
assert(!tr.classSymbol.is(Trait), i"$cls has bad parents $parents%, %")
733-
val tr1 = if (cls.is(Trait)) defn.ObjectType else tr
734-
tr1 :: trs1.filterNot(_.isAnyRef)
735-
case nil => nil
720+
case tp: PolyType =>
721+
this(tp.resultType)
722+
case tp @ ClassInfo(pre, cls, parents, decls, _) =>
723+
if (cls.is(Package)) tp
724+
else {
725+
def eraseParent(tp: Type) = tp.dealias match { // note: can't be opaque, since it's a class parent
726+
case tp: AppliedType if tp.tycon.isRef(defn.PairClass) => defn.ObjectType
727+
case _ => apply(tp).nn
736728
}
737-
var erasedDecls = decls.filteredScope(sym => !sym.isType || sym.isClass).openForMutations
738-
for dcl <- erasedDecls.iterator do
739-
if dcl.lastKnownDenotation.unforcedAnnotation(defn.TargetNameAnnot).isDefined
740-
&& dcl.targetName != dcl.name
741-
then
742-
if erasedDecls eq decls then erasedDecls = erasedDecls.cloneScope
743-
erasedDecls.unlink(dcl)
744-
erasedDecls.enter(dcl.targetName, dcl)
745-
val selfType1 = if cls.is(Module) then cls.sourceModule.termRef else NoType
746-
tp.derivedClassInfo(NoPrefix, erasedParents, erasedDecls, selfType1)
747-
// can't replace selftype by NoType because this would lose the sourceModule link
748-
}
749-
case _: ErrorType | JavaArrayType(_) =>
750-
tp
751-
case tp: WildcardType =>
752-
assert(inSigName, i"Cannot erase wildcard type $tp")
753-
null
754-
case tp if (tp `eq` NoType) || (tp `eq` NoPrefix) =>
755-
tp
756-
).ensuring(etp => etp != null || inSigName)
729+
val erasedParents: List[Type] =
730+
if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil
731+
else parents.mapConserve(eraseParent) match {
732+
case tr :: trs1 =>
733+
assert(!tr.classSymbol.is(Trait), i"$cls has bad parents $parents%, %")
734+
val tr1 = if (cls.is(Trait)) defn.ObjectType else tr
735+
tr1 :: trs1.filterNot(_.isAnyRef)
736+
case nil => nil
737+
}
738+
var erasedDecls = decls.filteredScope(sym => !sym.isType || sym.isClass).openForMutations
739+
for dcl <- erasedDecls.iterator do
740+
if dcl.lastKnownDenotation.unforcedAnnotation(defn.TargetNameAnnot).isDefined
741+
&& dcl.targetName != dcl.name
742+
then
743+
if erasedDecls eq decls then erasedDecls = erasedDecls.cloneScope
744+
erasedDecls.unlink(dcl)
745+
erasedDecls.enter(dcl.targetName, dcl)
746+
val selfType1 = if cls.is(Module) then cls.sourceModule.termRef else NoType
747+
tp.derivedClassInfo(NoPrefix, erasedParents, erasedDecls, selfType1)
748+
// can't replace selftype by NoType because this would lose the sourceModule link
749+
}
750+
case _: ErrorType | JavaArrayType(_) =>
751+
tp
752+
case tp: WildcardType =>
753+
assert(inSigName, i"Cannot erase wildcard type $tp")
754+
null
755+
case tp if (tp `eq` NoType) || (tp `eq` NoPrefix) =>
756+
tp
757+
assert(etp != null || inSigName, i"Unexpected null erasure for $tp")
758+
etp
757759

758760
/** Like translucentSuperType, but issue a fatal error if it does not exist. */
759761
private def checkedSuperType(tp: TypeProxy)(using Context): Type =

0 commit comments

Comments
 (0)