Skip to content

Commit 4499bc0

Browse files
committed
TypeErasure#apply: use WildcardType instead of null
As requested in the review, this is slightly shorter but means we have to be very careful to not accidentally forget to propagate a WildcardType to the top-level when computing signatures.
1 parent 2666e70 commit 4499bc0

File tree

1 file changed

+29
-35
lines changed

1 file changed

+29
-35
lines changed

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

+29-35
Original file line numberDiff line numberDiff line change
@@ -209,19 +209,19 @@ object TypeErasure {
209209
* @param tp The type to erase.
210210
*/
211211
def erasure(tp: Type)(using Context): Type =
212-
erasureFn(sourceLanguage = SourceLanguage.Scala3, semiEraseVCs = false, isConstructor = false, isSymbol = false, inSigName = false)(tp)(using preErasureCtx).nn
212+
erasureFn(sourceLanguage = SourceLanguage.Scala3, semiEraseVCs = false, isConstructor = false, isSymbol = false, inSigName = false)(tp)(using preErasureCtx)
213213

214214
/** The value class erasure of a Scala type, where value classes are semi-erased to
215215
* ErasedValueType (they will be fully erased in [[ElimErasedValueType]]).
216216
*
217217
* @param tp The type to erase.
218218
*/
219219
def valueErasure(tp: Type)(using Context): Type =
220-
erasureFn(sourceLanguage = SourceLanguage.Scala3, semiEraseVCs = true, isConstructor = false, isSymbol = false, inSigName = false)(tp)(using preErasureCtx).nn
220+
erasureFn(sourceLanguage = SourceLanguage.Scala3, semiEraseVCs = true, isConstructor = false, isSymbol = false, inSigName = false)(tp)(using preErasureCtx)
221221

222222
/** The erasure that Scala 2 would use for this type. */
223223
def scala2Erasure(tp: Type)(using Context): Type =
224-
erasureFn(sourceLanguage = SourceLanguage.Scala2, semiEraseVCs = true, isConstructor = false, isSymbol = false, inSigName = false)(tp)(using preErasureCtx).nn
224+
erasureFn(sourceLanguage = SourceLanguage.Scala2, semiEraseVCs = true, isConstructor = false, isSymbol = false, inSigName = false)(tp)(using preErasureCtx)
225225

226226
/** Like value class erasure, but value classes erase to their underlying type erasure */
227227
def fullErasure(tp: Type)(using Context): Type =
@@ -270,8 +270,8 @@ object TypeErasure {
270270
if (defn.isPolymorphicAfterErasure(sym)) eraseParamBounds(sym.info.asInstanceOf[PolyType])
271271
else if (sym.isAbstractType) TypeAlias(WildcardType)
272272
else if sym.is(ConstructorProxy) then NoType
273-
else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(using preErasureCtx).nn)
274-
else if (sym.is(Label)) erase.eraseResult(sym.info)(using preErasureCtx).nn
273+
else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(using preErasureCtx))
274+
else if (sym.is(Label)) erase.eraseResult(sym.info)(using preErasureCtx)
275275
else erase.eraseInfo(tp, sym)(using preErasureCtx) match {
276276
case einfo: MethodType =>
277277
if (sym.isGetter && einfo.resultType.isRef(defn.UnitClass))
@@ -596,10 +596,10 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
596596
*
597597
* If computing the erasure of T requires erasing a WildcardType or an
598598
* uninstantiated type variable, then an exception signaling an internal
599-
* error will be thrown, unless `inSigName` is set in which case `null`
599+
* error will be thrown, unless `inSigName` is set in which case WildcardType
600600
* will be returned.
601601
*
602-
* In all other situations, |T| will be non-null and computed as follow:
602+
* In all other situations, |T| will be computed as follow:
603603
* - For a refined type scala.Array+[T]:
604604
* - if T is Nothing or Null, []Object
605605
* - otherwise, if T <: Object, []|T|
@@ -631,7 +631,7 @@ 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 =
634+
private def apply(tp: Type)(using Context): Type =
635635
val etp = tp match
636636
case _: ErasedValueType =>
637637
tp
@@ -655,7 +655,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
655655
case SuperType(thistpe, supertpe) =>
656656
val eThis = this(thistpe)
657657
val eSuper = this(supertpe)
658-
if eThis == null || eSuper == null then null
658+
if eThis.isInstanceOf[WildcardType] || eSuper.isInstanceOf[WildcardType] then WildcardType
659659
else SuperType(eThis, eSuper)
660660
case ExprType(rt) =>
661661
defn.FunctionType(0)
@@ -665,7 +665,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
665665
eraseErasedFunctionApply(refinedInfo)
666666
case tp: TypeVar if !tp.isInstantiated =>
667667
assert(inSigName, i"Cannot erase uninstantiated type variable $tp")
668-
null
668+
WildcardType
669669
case tp: TypeProxy =>
670670
this(tp.underlying)
671671
case tp @ AndType(tp1, tp2) =>
@@ -676,7 +676,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
676676
else
677677
val e1 = this(tp1)
678678
val e2 = this(tp2)
679-
if e1 == null || e2 == null then null
679+
if e1.isInstanceOf[WildcardType] || e2.isInstanceOf[WildcardType] then WildcardType
680680
else erasedGlb(e1, e2)
681681
case OrType(tp1, tp2) =>
682682
if isSymbol && sourceLanguage.isScala2 && ctx.settings.scalajs.value then
@@ -693,11 +693,11 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
693693
else
694694
val e1 = this(tp1)
695695
val e2 = this(tp2)
696-
if e1 == null || e2 == null then null
696+
if e1.isInstanceOf[WildcardType] || e2.isInstanceOf[WildcardType] then WildcardType
697697
else TypeComparer.orType(e1, e2, isErased = true)
698698
case tp: MethodType =>
699699
def paramErasure(tpToErase: Type) =
700-
erasureFn(sourceLanguage, semiEraseVCs, isConstructor, isSymbol, inSigName = false)(tpToErase).nn
700+
erasureFn(sourceLanguage, semiEraseVCs, isConstructor, isSymbol, inSigName = false)(tpToErase)
701701
val (names, formals0) = if tp.hasErasedParams then
702702
tp.paramNames
703703
.zip(tp.paramInfos)
@@ -724,7 +724,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
724724
else {
725725
def eraseParent(tp: Type) = tp.dealias match { // note: can't be opaque, since it's a class parent
726726
case tp: AppliedType if tp.tycon.isRef(defn.PairClass) => defn.ObjectType
727-
case _ => apply(tp).nn
727+
case _ => apply(tp)
728728
}
729729
val erasedParents: List[Type] =
730730
if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil
@@ -751,10 +751,10 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
751751
tp
752752
case tp: WildcardType =>
753753
assert(inSigName, i"Cannot erase wildcard type $tp")
754-
null
754+
WildcardType
755755
case tp if (tp `eq` NoType) || (tp `eq` NoPrefix) =>
756756
tp
757-
assert(etp != null || inSigName, i"Unexpected null erasure for $tp")
757+
assert(!etp.isInstanceOf[WildcardType] || inSigName, i"Unexpected WildcardType erasure for $tp")
758758
etp
759759

760760
/** Like translucentSuperType, but issue a fatal error if it does not exist. */
@@ -788,15 +788,15 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
788788
else
789789
try
790790
val eElem = erasureFn(sourceLanguage, semiEraseVCs = false, isConstructor, isSymbol, inSigName)(elemtp)
791-
if eElem == null then null
791+
if eElem.isInstanceOf[WildcardType] then WildcardType
792792
else JavaArrayType(eElem)
793793
catch case ex: Throwable =>
794794
handleRecursive("erase array type", tp.show, ex)
795795
}
796796

797-
private def erasePair(tp: Type)(using Context): Type | Null = {
797+
private def erasePair(tp: Type)(using Context): Type = {
798798
val arity = tupleArity(tp)
799-
if arity == -2 then null // erasure depends on an uninstantiated type variable or WildcardType
799+
if arity == -2 then WildcardType // erasure depends on an uninstantiated type variable or WildcardType
800800
else if arity == -1 then defn.ProductClass.typeRef
801801
else if arity <= Definitions.MaxTupleArity then defn.TupleType(arity).nn
802802
else defn.TupleXXLClass.typeRef
@@ -807,13 +807,12 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
807807
* to the underlying type.
808808
*/
809809
def eraseInfo(tp: Type, sym: Symbol)(using Context): Type =
810-
assert(!inSigName) // therefore apply(...).nn won't fail
811810
val tp1 = tp match
812811
case tp: MethodicType => integrateContextResults(tp, contextResultCount(sym))
813812
case _ => tp
814813
tp1 match
815814
case ExprType(rt) =>
816-
if sym.is(Param) then apply(tp1).nn
815+
if sym.is(Param) then apply(tp1)
817816
// Note that params with ExprTypes are eliminated by ElimByName,
818817
// but potentially re-introduced by ResolveSuper, when we add
819818
// forwarders to mixin methods.
@@ -825,9 +824,9 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
825824
eraseResult(tp1.resultType) match
826825
case rt: MethodType => rt
827826
case rt => MethodType(Nil, Nil, rt)
828-
case tp1 => this(tp1).nn
827+
case tp1 => this(tp1)
829828

830-
private def eraseDerivedValueClass(tp: Type)(using Context): Type | Null = {
829+
private def eraseDerivedValueClass(tp: Type)(using Context): Type = {
831830
val cls = tp.classSymbol.asClass
832831
val unbox = valueClassUnbox(cls)
833832
if unbox.exists then
@@ -837,7 +836,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
837836
// The underlying part of an ErasedValueType cannot be an ErasedValueType itself
838837
val erase = erasureFn(sourceLanguage, semiEraseVCs = false, isConstructor, isSymbol, inSigName)
839838
val erasedUnderlying = erase(underlying)
840-
if erasedUnderlying == null then return null
839+
if erasedUnderlying.isInstanceOf[WildcardType] then return WildcardType
841840

842841
// Ideally, we would just use `erasedUnderlying` as the erasure of `tp`, but to
843842
// be binary-compatible with Scala 2 we need two special cases for polymorphic
@@ -871,7 +870,6 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
871870

872871
/** The erasure of a function result type. */
873872
def eraseResult(tp: Type)(using Context): Type =
874-
assert(!inSigName) // therefore apply(...).nn won't fail
875873
// For a value class V, "new V(x)" should have type V for type adaptation to work
876874
// correctly (see SIP-15 and [[Erasure.Boxing.adaptToType]]), so the result type of a
877875
// constructor method should not be semi-erased.
@@ -881,25 +879,24 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
881879
case tp: TypeRef =>
882880
val sym = tp.symbol
883881
if (sym eq defn.UnitClass) sym.typeRef
884-
else apply(tp).nn
882+
else apply(tp)
885883
case tp: AppliedType =>
886884
val sym = tp.tycon.typeSymbol
887885
if (sym.isClass && !erasureDependsOnArgs(sym)) eraseResult(tp.tycon)
888-
else apply(tp).nn
886+
else apply(tp)
889887
case _ =>
890-
apply(tp).nn
888+
apply(tp)
891889

892890
/** The name of the type as it is used in `Signature`s.
893891
*
894-
* If `tp` is null, or if computing its erasure requires erasing a
892+
* If `tp` is WildcardType, or if computing its erasure requires erasing a
895893
* WildcardType or an uninstantiated type variable, then the special name
896894
* `tpnme.Uninstantiated` which is used to signal an underdefined signature
897895
* is used.
898896
*
899897
* Note: Need to ensure correspondence with erasure!
900898
*/
901-
private def sigName(tp: Type | Null)(using Context): TypeName = try
902-
if tp == null then return tpnme.Uninstantiated
899+
private def sigName(tp: Type)(using Context): TypeName = try
903900
tp match {
904901
case tp: TypeRef =>
905902
if (!tp.denot.exists)
@@ -913,7 +910,6 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
913910
}
914911
if (semiEraseVCs && isDerivedValueClass(sym)) {
915912
val erasedVCRef = eraseDerivedValueClass(tp)
916-
if erasedVCRef == null then return tpnme.Uninstantiated
917913
if (erasedVCRef.exists) return sigName(erasedVCRef)
918914
}
919915
if (defn.isSyntheticFunctionClass(sym))
@@ -958,9 +954,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
958954
sigName(tp.underlying)
959955
case tp: WildcardType =>
960956
tpnme.Uninstantiated
961-
case tp: ErrorType =>
962-
tpnme.ERROR
963-
case _ if tp eq NoType => // Can't write `case NoType` because of #18083.
957+
case _: ErrorType | NoType =>
964958
tpnme.ERROR
965959
case _ =>
966960
val erasedTp = this(tp)

0 commit comments

Comments
 (0)