Skip to content

Commit 35911f3

Browse files
committed
Add classSymbolAfterErasure
1 parent 54c7f9f commit 35911f3

File tree

5 files changed

+56
-33
lines changed

5 files changed

+56
-33
lines changed

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import core._
1010
import util.Spans._, Types._, Contexts._, Constants._, Names._, Flags._, NameOps._
1111
import Symbols._, StdNames._, Annotations._, Trees._, Symbols._
1212
import Decorators._, DenotTransformers._
13+
import NullOpsDecorator._
1314
import collection.{immutable, mutable}
1415
import util.{Property, SourceFile, NoSource}
1516
import NameKinds.{TempResultName, OuterSelectName}
@@ -472,7 +473,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
472473

473474
/** The wrapped array method name for an array of type elemtp */
474475
def wrapArrayMethodName(elemtp: Type)(using Context): TermName = {
475-
val elemCls = elemtp.classSymbol
476+
val elemCls = elemtp.classSymbolAfterErasure
476477
if (elemCls.isPrimitiveValueClass) nme.wrapXArray(elemCls.name)
477478
else if (elemCls.derivesFrom(defn.ObjectClass) && !elemCls.isNotRuntimeClass) nme.wrapRefArray
478479
else nme.genericWrapArray

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,6 @@ object NullOpsDecorator {
7474
def isNullableAfterErasure(using Context): Boolean =
7575
self.isNullType
7676
|| !self.isNothingType
77-
&& self.derivesFrom(defn.ObjectClass, afterErasure = true)
77+
&& self.derivesFrom(defn.ObjectClass, isErased = true)
7878
}
7979
}

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -247,15 +247,15 @@ object TypeErasure {
247247
def isUnboundedGeneric(tp: Type)(using Context): Boolean = tp.dealias match {
248248
case tp: TypeRef if !tp.symbol.isOpaqueAlias =>
249249
!tp.symbol.isClass &&
250-
!classify(tp).derivesFrom(defn.ObjectClass, afterErasure = true) &&
250+
!classify(tp).derivesFrom(defn.ObjectClass, isErased = true) &&
251251
!tp.symbol.is(JavaDefined)
252252
case tp: TypeParamRef =>
253-
!classify(tp).derivesFrom(defn.ObjectClass, afterErasure = true) &&
253+
!classify(tp).derivesFrom(defn.ObjectClass, isErased = true) &&
254254
!tp.binder.resultType.isJavaMethod
255255
case tp: TypeAlias => isUnboundedGeneric(tp.alias)
256256
case tp: TypeBounds =>
257257
val upper = classify(tp.hi)
258-
!upper.derivesFrom(defn.ObjectClass, afterErasure = true) &&
258+
!upper.derivesFrom(defn.ObjectClass, isErased = true) &&
259259
!upper.isPrimitiveValueType
260260
case tp: TypeProxy => isUnboundedGeneric(tp.translucentSuperType)
261261
case tp: AndType => isUnboundedGeneric(tp.tp1) && isUnboundedGeneric(tp.tp2)
@@ -292,8 +292,8 @@ object TypeErasure {
292292
// We need to short-circuit this case here because the regular lub logic below
293293
// relies on the class hierarchy, which doesn't properly capture `Null`s subtyping
294294
// behaviour.
295-
if (tp1.isBottomTypeAfterErasure && tp2.derivesFrom(defn.ObjectClass, afterErasure = true)) return tp2
296-
if (tp2.isBottomTypeAfterErasure && tp1.derivesFrom(defn.ObjectClass, afterErasure = true)) return tp1
295+
if (tp1.isBottomTypeAfterErasure && tp2.derivesFrom(defn.ObjectClass, isErased = true)) return tp2
296+
if (tp2.isBottomTypeAfterErasure && tp1.derivesFrom(defn.ObjectClass, isErased = true)) return tp1
297297
tp1 match {
298298
case JavaArrayType(elem1) =>
299299
import dotty.tools.dotc.transform.TypeUtils._

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

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,11 @@ object Types {
263263
/** True if this type is an instance of the given `cls` or an instance of
264264
* a non-bottom subclass of `cls`.
265265
*/
266-
final def derivesFrom(cls: Symbol, afterErasure: Boolean = false)(using Context): Boolean = {
266+
final def derivesFrom(cls: Symbol, isErased: Boolean = false)(using Context): Boolean = {
267+
def isLowerBottomType(tp: Type) =
268+
(if isErased then tp.isBottomTypeAfterErasure else tp.isBottomType)
269+
&& (tp.hasClassSymbol(defn.NothingClass)
270+
|| cls != defn.NothingClass && !cls.isValueClass)
267271
def loop(tp: Type): Boolean = tp match {
268272
case tp: TypeRef =>
269273
val sym = tp.symbol
@@ -280,10 +284,6 @@ object Types {
280284
// If the type is `T | Null` or `T | Nothing`, the class is != Nothing,
281285
// and `T` derivesFrom the class, then the OrType derivesFrom the class.
282286
// Otherwise, we need to check both sides derivesFrom the class.
283-
def isLowerBottomType(tp: Type) =
284-
(if afterErasure then t.isBottomTypeAfterErasure else t.isBottomType)
285-
&& (tp.hasClassSymbol(defn.NothingClass)
286-
|| cls != defn.NothingClass && !cls.isValueClass)
287287
if isLowerBottomType(tp.tp1) then
288288
loop(tp.tp2)
289289
else if isLowerBottomType(tp.tp2) then
@@ -467,28 +467,45 @@ object Types {
467467
* instance, or NoSymbol if none exists (either because this type is not a
468468
* value type, or because superclasses are ambiguous).
469469
*/
470-
final def classSymbol(using Context): Symbol = this match {
471-
case tp: TypeRef =>
472-
val sym = tp.symbol
473-
if (sym.isClass) sym else tp.superType.classSymbol
474-
case tp: TypeProxy =>
475-
tp.underlying.classSymbol
476-
case tp: ClassInfo =>
477-
tp.cls
478-
case AndType(l, r) =>
479-
val lsym = l.classSymbol
480-
val rsym = r.classSymbol
481-
if (lsym isSubClass rsym) lsym
482-
else if (rsym isSubClass lsym) rsym
483-
else NoSymbol
484-
case tp: OrType =>
485-
tp.join.classSymbol
486-
case _: JavaArrayType =>
487-
defn.ArrayClass
488-
case _ =>
489-
NoSymbol
490-
}
470+
final def classSymbol(using Context): Symbol = classSymbolWith(false)
471+
final def classSymbolAfterErasure(using Context): Symbol = classSymbolWith(true)
472+
473+
final private def classSymbolWith(isErased: Boolean)(using Context): Symbol = {
474+
def loop(tp:Type): Symbol = tp match {
475+
case tp: TypeRef =>
476+
val sym = tp.symbol
477+
if (sym.isClass) sym else loop(tp.superType)
478+
case tp: TypeProxy =>
479+
loop(tp.underlying)
480+
case tp: ClassInfo =>
481+
tp.cls
482+
case AndType(l, r) =>
483+
val lsym = loop(l)
484+
val rsym = loop(r)
485+
if (lsym isSubClass rsym) lsym
486+
else if (rsym isSubClass lsym) rsym
487+
else NoSymbol
488+
case tp: OrType =>
489+
if tp.tp1.hasClassSymbol(defn.NothingClass) then
490+
loop(tp.tp2)
491+
else if tp.tp2.hasClassSymbol(defn.NothingClass) then
492+
loop(tp.tp1)
493+
else
494+
val tp1Null = tp.tp1.hasClassSymbol(defn.NullClass)
495+
val tp2Null = tp.tp2.hasClassSymbol(defn.NullClass)
496+
if isErased && (tp1Null || tp2Null) then
497+
val otherSide = if tp1Null then loop(tp.tp2) else loop(tp.tp1)
498+
if otherSide.isValueClass then defn.AnyClass else otherSide
499+
else
500+
loop(tp.join)
501+
case _: JavaArrayType =>
502+
defn.ArrayClass
503+
case _ =>
504+
NoSymbol
505+
}
491506

507+
loop(this)
508+
}
492509
/** The least (wrt <:<) set of symbols satisfying the `include` predicate of which this type is a subtype
493510
*/
494511
final def parentSymbols(include: Symbol => Boolean)(using Context): List[Symbol] = this match {

tests/explicit-nulls/unsafe-common/unsafe-cast.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,9 @@ class S {
6565
n1(Array("a", null))
6666
n2(Array("a", null))
6767
}
68+
69+
locally {
70+
val os: Option[String] = None
71+
val s: String = os.orNull // error
72+
}
6873
}

0 commit comments

Comments
 (0)