@@ -83,9 +83,11 @@ object TypeErasure {
83
83
* `EmptyTuple.type` because of a missing dealias, but this is now
84
84
* impossible to fix.
85
85
*
86
- * @return The arity if it can be determined or -1 otherwise.
86
+ * @return The arity if it can be determined, or:
87
+ * -1 if this type does not have a fixed arity
88
+ * -2 if the arity depends on an uninstantiated type variable or WildcardType.
87
89
*/
88
- def tupleArity (tp : Type )(using Context ): Int = tp/* .dealias*/ match {
90
+ def tupleArity (tp : Type )(using Context ): Int = tp/* .dealias*/ match
89
91
case AppliedType (tycon, _ :: tl :: Nil ) if tycon.isRef(defn.PairClass ) =>
90
92
val arity = tupleArity(tl)
91
93
if (arity < 0 ) arity else arity + 1
@@ -94,11 +96,14 @@ object TypeErasure {
94
96
case tp : AndOrType =>
95
97
val arity1 = tupleArity(tp.tp1)
96
98
val arity2 = tupleArity(tp.tp2)
97
- if arity1 == arity2 then arity1 else - 1
99
+ if arity1 == arity2 then arity1 else math.min(- 1 , math.min(arity1, arity2))
100
+ case tp : WildcardType => - 2
101
+ case tp : TypeVar if ! tp.isInstantiated => - 2
98
102
case _ =>
99
103
if defn.isTupleNType(tp) then tp.dealias.argInfos.length
100
- else - 1
101
- }
104
+ else tp.dealias match
105
+ case tp : TypeVar if ! tp.isInstantiated => - 2
106
+ case _ => - 1
102
107
103
108
def normalizeClass (cls : ClassSymbol )(using Context ): ClassSymbol = {
104
109
if (cls.owner == defn.ScalaPackageClass ) {
@@ -204,19 +209,19 @@ object TypeErasure {
204
209
* @param tp The type to erase.
205
210
*/
206
211
def erasure (tp : Type )(using Context ): Type =
207
- erasureFn(sourceLanguage = SourceLanguage .Scala3 , semiEraseVCs = false , isConstructor = false , isSymbol = false , inSigName = false )(tp)(using preErasureCtx)
212
+ erasureFn(sourceLanguage = SourceLanguage .Scala3 , semiEraseVCs = false , isConstructor = false , isSymbol = false , inSigName = false )(tp)(using preErasureCtx).nn
208
213
209
214
/** The value class erasure of a Scala type, where value classes are semi-erased to
210
215
* ErasedValueType (they will be fully erased in [[ElimErasedValueType ]]).
211
216
*
212
217
* @param tp The type to erase.
213
218
*/
214
219
def valueErasure (tp : Type )(using Context ): Type =
215
- erasureFn(sourceLanguage = SourceLanguage .Scala3 , semiEraseVCs = true , isConstructor = false , isSymbol = false , inSigName = false )(tp)(using preErasureCtx)
220
+ erasureFn(sourceLanguage = SourceLanguage .Scala3 , semiEraseVCs = true , isConstructor = false , isSymbol = false , inSigName = false )(tp)(using preErasureCtx).nn
216
221
217
222
/** The erasure that Scala 2 would use for this type. */
218
223
def scala2Erasure (tp : Type )(using Context ): Type =
219
- erasureFn(sourceLanguage = SourceLanguage .Scala2 , semiEraseVCs = true , isConstructor = false , isSymbol = false , inSigName = false )(tp)(using preErasureCtx)
224
+ erasureFn(sourceLanguage = SourceLanguage .Scala2 , semiEraseVCs = true , isConstructor = false , isSymbol = false , inSigName = false )(tp)(using preErasureCtx).nn
220
225
221
226
/** Like value class erasure, but value classes erase to their underlying type erasure */
222
227
def fullErasure (tp : Type )(using Context ): Type =
@@ -265,8 +270,8 @@ object TypeErasure {
265
270
if (defn.isPolymorphicAfterErasure(sym)) eraseParamBounds(sym.info.asInstanceOf [PolyType ])
266
271
else if (sym.isAbstractType) TypeAlias (WildcardType )
267
272
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)
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
270
275
else erase.eraseInfo(tp, sym)(using preErasureCtx) match {
271
276
case einfo : MethodType =>
272
277
if (sym.isGetter && einfo.resultType.isRef(defn.UnitClass ))
@@ -587,8 +592,14 @@ import TypeErasure._
587
592
*/
588
593
class TypeErasure (sourceLanguage : SourceLanguage , semiEraseVCs : Boolean , isConstructor : Boolean , isSymbol : Boolean , inSigName : Boolean ) {
589
594
590
- /** The erasure |T| of a type T. This is:
595
+ /** The erasure |T| of a type T.
596
+ *
597
+ * If computing the erasure of T requires erasing a WildcardType or an
598
+ * uninstantiated type variable, then an exception signaling an internal
599
+ * error will be thrown, unless `inSigName` is set in which case `null`
600
+ * will be returned.
591
601
*
602
+ * In all other situations, |T| will be non-null and computed as follow:
592
603
* - For a refined type scala.Array+[T]:
593
604
* - if T is Nothing or Null, []Object
594
605
* - otherwise, if T <: Object, []|T|
@@ -620,7 +631,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
620
631
* - For NoType or NoPrefix, the type itself.
621
632
* - For any other type, exception.
622
633
*/
623
- private def apply (tp : Type )(using Context ): Type = tp match {
634
+ private def apply (tp : Type )(using Context ): Type | Null = ( tp match
624
635
case _ : ErasedValueType =>
625
636
tp
626
637
case tp : TypeRef =>
@@ -641,13 +652,19 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
641
652
case _ : ThisType =>
642
653
this (tp.widen)
643
654
case SuperType (thistpe, supertpe) =>
644
- SuperType (this (thistpe), this (supertpe))
655
+ val eThis = this (thistpe)
656
+ val eSuper = this (supertpe)
657
+ if eThis == null || eSuper == null then null
658
+ else SuperType (eThis, eSuper)
645
659
case ExprType (rt) =>
646
660
defn.FunctionType (0 )
647
661
case RefinedType (parent, nme.apply, refinedInfo) if parent.typeSymbol eq defn.PolyFunctionClass =>
648
662
erasePolyFunctionApply(refinedInfo)
649
663
case RefinedType (parent, nme.apply, refinedInfo : MethodType ) if defn.isErasedFunctionType(parent) =>
650
664
eraseErasedFunctionApply(refinedInfo)
665
+ case tp : TypeVar if ! tp.isInstantiated =>
666
+ assert(inSigName, i " Cannot erase uninstantiated type variable $tp" )
667
+ null
651
668
case tp : TypeProxy =>
652
669
this (tp.underlying)
653
670
case tp @ AndType (tp1, tp2) =>
@@ -656,7 +673,10 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
656
673
else if sourceLanguage.isScala2 then
657
674
this (Scala2Erasure .intersectionDominator(Scala2Erasure .flattenedParents(tp)))
658
675
else
659
- erasedGlb(this (tp1), this (tp2))
676
+ val e1 = this (tp1)
677
+ val e2 = this (tp2)
678
+ if e1 == null || e2 == null then null
679
+ else erasedGlb(e1, e2)
660
680
case OrType (tp1, tp2) =>
661
681
if isSymbol && sourceLanguage.isScala2 && ctx.settings.scalajs.value then
662
682
// In Scala2Unpickler we unpickle Scala.js pseudo-unions as if they were
@@ -670,10 +690,13 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
670
690
// alone (and this doesn't impact the SJSIR we generate).
671
691
JSDefinitions .jsdefn.PseudoUnionType
672
692
else
673
- TypeComparer .orType(this (tp1), this (tp2), isErased = true )
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 )
674
697
case tp : MethodType =>
675
698
def paramErasure (tpToErase : Type ) =
676
- erasureFn(sourceLanguage, semiEraseVCs, isConstructor, isSymbol, inSigName)(tpToErase)
699
+ erasureFn(sourceLanguage, semiEraseVCs, isConstructor, isSymbol, inSigName = false )(tpToErase).nn
677
700
val (names, formals0) = if tp.hasErasedParams then
678
701
tp.paramNames
679
702
.zip(tp.paramInfos)
@@ -700,7 +723,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
700
723
else {
701
724
def eraseParent (tp : Type ) = tp.dealias match { // note: can't be opaque, since it's a class parent
702
725
case tp : AppliedType if tp.tycon.isRef(defn.PairClass ) => defn.ObjectType
703
- case _ => apply(tp)
726
+ case _ => apply(tp).nn
704
727
}
705
728
val erasedParents : List [Type ] =
706
729
if ((cls eq defn.ObjectClass ) || cls.isPrimitiveValueClass) Nil
@@ -725,11 +748,12 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
725
748
}
726
749
case _ : ErrorType | JavaArrayType (_) =>
727
750
tp
728
- case tp : WildcardType if inSigName =>
729
- tp
751
+ case tp : WildcardType =>
752
+ assert(inSigName, i " Cannot erase wildcard type $tp" )
753
+ null
730
754
case tp if (tp `eq` NoType ) || (tp `eq` NoPrefix ) =>
731
755
tp
732
- }
756
+ ).ensuring(etp => etp != null || inSigName)
733
757
734
758
/** Like translucentSuperType, but issue a fatal error if it does not exist. */
735
759
private def checkedSuperType (tp : TypeProxy )(using Context ): Type =
@@ -760,15 +784,19 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
760
784
val defn .ArrayOf (elemtp) = tp : @ unchecked
761
785
if (isGenericArrayElement(elemtp, isScala2 = sourceLanguage.isScala2)) defn.ObjectType
762
786
else
763
- try JavaArrayType (erasureFn(sourceLanguage, semiEraseVCs = false , isConstructor, isSymbol, inSigName)(elemtp))
787
+ try
788
+ val eElem = erasureFn(sourceLanguage, semiEraseVCs = false , isConstructor, isSymbol, inSigName)(elemtp)
789
+ if eElem == null then null
790
+ else JavaArrayType (eElem)
764
791
catch case ex : Throwable =>
765
792
handleRecursive(" erase array type" , tp.show, ex)
766
793
}
767
794
768
- private def erasePair (tp : Type )(using Context ): Type = {
795
+ private def erasePair (tp : Type )(using Context ): Type | Null = {
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