@@ -85,7 +85,7 @@ object TypeErasure {
85
85
*
86
86
* @return The arity if it can be determined or -1 otherwise.
87
87
*/
88
- def tupleArity (tp : Type )(using Context ): Int = tp/* .dealias*/ match {
88
+ def tupleArity (tp : Type )(using Context ): Int = tp/* .dealias*/ match
89
89
case AppliedType (tycon, _ :: tl :: Nil ) if tycon.isRef(defn.PairClass ) =>
90
90
val arity = tupleArity(tl)
91
91
if (arity < 0 ) arity else arity + 1
@@ -94,11 +94,14 @@ object TypeErasure {
94
94
case tp : AndOrType =>
95
95
val arity1 = tupleArity(tp.tp1)
96
96
val arity2 = tupleArity(tp.tp2)
97
- if arity1 == arity2 then arity1 else - 1
97
+ if arity1 == arity2 then arity1 else math.min(- 1 , math.min(arity1, arity2))
98
+ case tp : WildcardType => - 2
99
+ case tp : TypeVar if ! tp.isInstantiated => - 2
98
100
case _ =>
99
101
if defn.isTupleNType(tp) then tp.dealias.argInfos.length
100
- else - 1
101
- }
102
+ else tp.dealias match
103
+ case tp : TypeVar if ! tp.isInstantiated => - 2
104
+ case _ => - 1
102
105
103
106
def normalizeClass (cls : ClassSymbol )(using Context ): ClassSymbol = {
104
107
if (cls.owner == defn.ScalaPackageClass ) {
@@ -204,19 +207,19 @@ object TypeErasure {
204
207
* @param tp The type to erase.
205
208
*/
206
209
def erasure (tp : Type )(using Context ): Type =
207
- erasureFn(sourceLanguage = SourceLanguage .Scala3 , semiEraseVCs = false , isConstructor = false , isSymbol = false , inSigName = false )(tp)(using preErasureCtx)
210
+ erasureFn(sourceLanguage = SourceLanguage .Scala3 , semiEraseVCs = false , isConstructor = false , isSymbol = false , inSigName = false )(tp)(using preErasureCtx).nn
208
211
209
212
/** The value class erasure of a Scala type, where value classes are semi-erased to
210
213
* ErasedValueType (they will be fully erased in [[ElimErasedValueType ]]).
211
214
*
212
215
* @param tp The type to erase.
213
216
*/
214
217
def valueErasure (tp : Type )(using Context ): Type =
215
- erasureFn(sourceLanguage = SourceLanguage .Scala3 , semiEraseVCs = true , isConstructor = false , isSymbol = false , inSigName = false )(tp)(using preErasureCtx)
218
+ erasureFn(sourceLanguage = SourceLanguage .Scala3 , semiEraseVCs = true , isConstructor = false , isSymbol = false , inSigName = false )(tp)(using preErasureCtx).nn
216
219
217
220
/** The erasure that Scala 2 would use for this type. */
218
221
def scala2Erasure (tp : Type )(using Context ): Type =
219
- erasureFn(sourceLanguage = SourceLanguage .Scala2 , semiEraseVCs = true , isConstructor = false , isSymbol = false , inSigName = false )(tp)(using preErasureCtx)
222
+ erasureFn(sourceLanguage = SourceLanguage .Scala2 , semiEraseVCs = true , isConstructor = false , isSymbol = false , inSigName = false )(tp)(using preErasureCtx).nn
220
223
221
224
/** Like value class erasure, but value classes erase to their underlying type erasure */
222
225
def fullErasure (tp : Type )(using Context ): Type =
@@ -265,8 +268,8 @@ object TypeErasure {
265
268
if (defn.isPolymorphicAfterErasure(sym)) eraseParamBounds(sym.info.asInstanceOf [PolyType ])
266
269
else if (sym.isAbstractType) TypeAlias (WildcardType )
267
270
else if sym.is(ConstructorProxy ) then NoType
268
- else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(using preErasureCtx))
269
- else if (sym.is(Label )) erase.eraseResult(sym.info)(using preErasureCtx)
271
+ else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(using preErasureCtx).nn )
272
+ else if (sym.is(Label )) erase.eraseResult(sym.info)(using preErasureCtx).nn
270
273
else erase.eraseInfo(tp, sym)(using preErasureCtx) match {
271
274
case einfo : MethodType =>
272
275
if (sym.isGetter && einfo.resultType.isRef(defn.UnitClass ))
@@ -587,8 +590,14 @@ import TypeErasure._
587
590
*/
588
591
class TypeErasure (sourceLanguage : SourceLanguage , semiEraseVCs : Boolean , isConstructor : Boolean , isSymbol : Boolean , inSigName : Boolean ) {
589
592
590
- /** The erasure |T| of a type T. This is:
593
+ /** The erasure |T| of a type T.
594
+ *
595
+ * If computing the erasure of T requires erasing a WildcardType or an
596
+ * uninstantiated type variable, then an exception signaling an internal
597
+ * error will be thrown, unless `inSigName` is set in which case `null`
598
+ * will be returned.
591
599
*
600
+ * In all other situations, |T| will be non-null and computed as follow:
592
601
* - For a refined type scala.Array+[T]:
593
602
* - if T is Nothing or Null, []Object
594
603
* - otherwise, if T <: Object, []|T|
@@ -620,7 +629,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
620
629
* - For NoType or NoPrefix, the type itself.
621
630
* - For any other type, exception.
622
631
*/
623
- private def apply (tp : Type )(using Context ): Type = tp match {
632
+ private def apply (tp : Type )(using Context ): Type | Null = ( tp match
624
633
case _ : ErasedValueType =>
625
634
tp
626
635
case tp : TypeRef =>
@@ -641,13 +650,19 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
641
650
case _ : ThisType =>
642
651
this (tp.widen)
643
652
case SuperType (thistpe, supertpe) =>
644
- SuperType (this (thistpe), this (supertpe))
653
+ val eThis = this (thistpe)
654
+ val eSuper = this (supertpe)
655
+ if eThis == null || eSuper == null then null
656
+ else SuperType (eThis, eSuper)
645
657
case ExprType (rt) =>
646
658
defn.FunctionType (0 )
647
659
case RefinedType (parent, nme.apply, refinedInfo) if parent.typeSymbol eq defn.PolyFunctionClass =>
648
660
erasePolyFunctionApply(refinedInfo)
649
661
case RefinedType (parent, nme.apply, refinedInfo : MethodType ) if defn.isErasedFunctionType(parent) =>
650
662
eraseErasedFunctionApply(refinedInfo)
663
+ case tp : TypeVar if ! tp.isInstantiated =>
664
+ assert(inSigName, i " Cannot erase uninstantiated type variable $tp" )
665
+ null
651
666
case tp : TypeProxy =>
652
667
this (tp.underlying)
653
668
case tp @ AndType (tp1, tp2) =>
@@ -656,7 +671,10 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
656
671
else if sourceLanguage.isScala2 then
657
672
this (Scala2Erasure .intersectionDominator(Scala2Erasure .flattenedParents(tp)))
658
673
else
659
- erasedGlb(this (tp1), this (tp2))
674
+ val e1 = this (tp1)
675
+ val e2 = this (tp2)
676
+ if e1 == null || e2 == null then null
677
+ else erasedGlb(e1, e2)
660
678
case OrType (tp1, tp2) =>
661
679
if isSymbol && sourceLanguage.isScala2 && ctx.settings.scalajs.value then
662
680
// In Scala2Unpickler we unpickle Scala.js pseudo-unions as if they were
@@ -670,10 +688,13 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
670
688
// alone (and this doesn't impact the SJSIR we generate).
671
689
JSDefinitions .jsdefn.PseudoUnionType
672
690
else
673
- TypeComparer .orType(this (tp1), this (tp2), isErased = true )
691
+ val e1 = this (tp1)
692
+ val e2 = this (tp2)
693
+ if e1 == null || e2 == null then null
694
+ else TypeComparer .orType(e1, e2, isErased = true )
674
695
case tp : MethodType =>
675
696
def paramErasure (tpToErase : Type ) =
676
- erasureFn(sourceLanguage, semiEraseVCs, isConstructor, isSymbol, inSigName)(tpToErase)
697
+ erasureFn(sourceLanguage, semiEraseVCs, isConstructor, isSymbol, inSigName = false )(tpToErase).nn
677
698
val (names, formals0) = if tp.hasErasedParams then
678
699
tp.paramNames
679
700
.zip(tp.paramInfos)
@@ -700,7 +721,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
700
721
else {
701
722
def eraseParent (tp : Type ) = tp.dealias match { // note: can't be opaque, since it's a class parent
702
723
case tp : AppliedType if tp.tycon.isRef(defn.PairClass ) => defn.ObjectType
703
- case _ => apply(tp)
724
+ case _ => apply(tp).nn
704
725
}
705
726
val erasedParents : List [Type ] =
706
727
if ((cls eq defn.ObjectClass ) || cls.isPrimitiveValueClass) Nil
@@ -725,11 +746,12 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
725
746
}
726
747
case _ : ErrorType | JavaArrayType (_) =>
727
748
tp
728
- case tp : WildcardType if inSigName =>
749
+ case tp : WildcardType =>
750
+ assert(inSigName, i " Cannot erase wildcard type $tp" )
751
+ null
752
+ case tp if (tp `eq` NoType ) || (tp `eq` NoPrefix ) => // Why is this check different?
729
753
tp
730
- case tp if (tp `eq` NoType ) || (tp `eq` NoPrefix ) =>
731
- tp
732
- }
754
+ ).ensuring(etp => etp != null || inSigName)
733
755
734
756
/** Like translucentSuperType, but issue a fatal error if it does not exist. */
735
757
private def checkedSuperType (tp : TypeProxy )(using Context ): Type =
@@ -760,15 +782,21 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
760
782
val defn .ArrayOf (elemtp) = tp : @ unchecked
761
783
if (isGenericArrayElement(elemtp, isScala2 = sourceLanguage.isScala2)) defn.ObjectType
762
784
else
763
- try JavaArrayType (erasureFn(sourceLanguage, semiEraseVCs = false , isConstructor, isSymbol, inSigName)(elemtp))
785
+ try
786
+ val eElem = erasureFn(sourceLanguage, semiEraseVCs = false , isConstructor, isSymbol, inSigName)(elemtp)
787
+ if eElem == null then null
788
+ else JavaArrayType (eElem)
764
789
catch case ex : Throwable =>
765
790
handleRecursive(" erase array type" , tp.show, ex)
766
791
}
767
792
768
- private def erasePair (tp : Type )(using Context ): Type = {
793
+ private def erasePair (tp : Type )(using Context ): Type | Null = {
794
+ // NOTE: `tupleArity` does not consider TypeRef(EmptyTuple$) equivalent to EmptyTuple.type,
795
+ // we fix this for printers, but type erasure should be preserved.
769
796
val arity = tupleArity(tp)
770
- if (arity < 0 ) defn.ProductClass .typeRef
771
- else if (arity <= Definitions .MaxTupleArity ) defn.TupleType (arity).nn
797
+ if arity == - 2 then null // erasure depends on an uninstantiated type variable or WildcardType
798
+ else if arity == - 1 then defn.ProductClass .typeRef
799
+ else if arity <= Definitions .MaxTupleArity then defn.TupleType (arity).nn
772
800
else defn.TupleXXLClass .typeRef
773
801
}
774
802
@@ -777,12 +805,13 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
777
805
* to the underlying type.
778
806
*/
779
807
def eraseInfo (tp : Type , sym : Symbol )(using Context ): Type =
808
+ assert(! inSigName) // therefore apply(...).nn won't fail
780
809
val tp1 = tp match
781
810
case tp : MethodicType => integrateContextResults(tp, contextResultCount(sym))
782
811
case _ => tp
783
812
tp1 match
784
813
case ExprType (rt) =>
785
- if sym.is(Param ) then apply(tp1)
814
+ if sym.is(Param ) then apply(tp1).nn
786
815
// Note that params with ExprTypes are eliminated by ElimByName,
787
816
// but potentially re-introduced by ResolveSuper, when we add
788
817
// forwarders to mixin methods.
@@ -794,9 +823,9 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
794
823
eraseResult(tp1.resultType) match
795
824
case rt : MethodType => rt
796
825
case rt => MethodType (Nil , Nil , rt)
797
- case tp1 => this (tp1)
826
+ case tp1 => this (tp1).nn
798
827
799
- private def eraseDerivedValueClass (tp : Type )(using Context ): Type = {
828
+ private def eraseDerivedValueClass (tp : Type )(using Context ): Type | Null = {
800
829
val cls = tp.classSymbol.asClass
801
830
val unbox = valueClassUnbox(cls)
802
831
if unbox.exists then
@@ -806,6 +835,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
806
835
// The underlying part of an ErasedValueType cannot be an ErasedValueType itself
807
836
val erase = erasureFn(sourceLanguage, semiEraseVCs = false , isConstructor, isSymbol, inSigName)
808
837
val erasedUnderlying = erase(underlying)
838
+ if erasedUnderlying == null then return null
809
839
810
840
// Ideally, we would just use `erasedUnderlying` as the erasure of `tp`, but to
811
841
// be binary-compatible with Scala 2 we need two special cases for polymorphic
@@ -839,6 +869,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
839
869
840
870
/** The erasure of a function result type. */
841
871
def eraseResult (tp : Type )(using Context ): Type =
872
+ assert(! inSigName) // therefore apply(...).nn won't fail
842
873
// For a value class V, "new V(x)" should have type V for type adaptation to work
843
874
// correctly (see SIP-15 and [[Erasure.Boxing.adaptToType]]), so the result type of a
844
875
// constructor method should not be semi-erased.
@@ -848,18 +879,25 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
848
879
case tp : TypeRef =>
849
880
val sym = tp.symbol
850
881
if (sym eq defn.UnitClass ) sym.typeRef
851
- else this (tp)
882
+ else apply (tp).nn
852
883
case tp : AppliedType =>
853
884
val sym = tp.tycon.typeSymbol
854
885
if (sym.isClass && ! erasureDependsOnArgs(sym)) eraseResult(tp.tycon)
855
- else this (tp)
886
+ else apply (tp).nn
856
887
case _ =>
857
- this (tp)
888
+ apply (tp).nn
858
889
859
890
/** The name of the type as it is used in `Signature`s.
860
- * Need to ensure correspondence with erasure!
891
+ *
892
+ * If `tp` is null, or if computing its erasure requires erasing a
893
+ * WildcardType or an uninstantiated type variable, then the special name
894
+ * `tpnme.Uninstantiated` which is used to signal an underdefined signature
895
+ * is used.
896
+ *
897
+ * Note: Need to ensure correspondence with erasure!
861
898
*/
862
- private def sigName (tp : Type )(using Context ): TypeName = try
899
+ private def sigName (tp : Type | Null )(using Context ): TypeName = try
900
+ if tp == null then return tpnme.Uninstantiated
863
901
tp match {
864
902
case tp : TypeRef =>
865
903
if (! tp.denot.exists)
@@ -873,6 +911,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
873
911
}
874
912
if (semiEraseVCs && isDerivedValueClass(sym)) {
875
913
val erasedVCRef = eraseDerivedValueClass(tp)
914
+ if erasedVCRef == null then return tpnme.Uninstantiated
876
915
if (erasedVCRef.exists) return sigName(erasedVCRef)
877
916
}
878
917
if (defn.isSyntheticFunctionClass(sym))
@@ -897,14 +936,15 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
897
936
case ErasedValueType (_, underlying) =>
898
937
sigName(underlying)
899
938
case JavaArrayType (elem) =>
900
- sigName(elem) ++ " []"
939
+ val elemName = sigName(elem)
940
+ if elemName eq tpnme.Uninstantiated then elemName
941
+ else elemName ++ " []"
901
942
case tp : TermRef =>
902
943
sigName(underlyingOfTermRef(tp))
903
944
case ExprType (rt) =>
904
945
sigName(defn.FunctionOf (Nil , rt))
905
- case tp : TypeVar =>
906
- val inst = tp.instanceOpt
907
- if (inst.exists) sigName(inst) else tpnme.Uninstantiated
946
+ case tp : TypeVar if ! tp.isInstantiated =>
947
+ tpnme.Uninstantiated
908
948
case tp @ RefinedType (parent, nme.apply, _) if parent.typeSymbol eq defn.PolyFunctionClass =>
909
949
// we need this case rather than falling through to the default
910
950
// because RefinedTypes <: TypeProxy and it would be caught by
@@ -916,7 +956,9 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
916
956
sigName(tp.underlying)
917
957
case tp : WildcardType =>
918
958
tpnme.Uninstantiated
919
- case _ : ErrorType | NoType =>
959
+ case tp : ErrorType =>
960
+ tpnme.ERROR
961
+ case _ if tp eq NoType => // Can't write `case NoType` because of #18083.
920
962
tpnme.ERROR
921
963
case _ =>
922
964
val erasedTp = this (tp)
0 commit comments