Skip to content

Commit 3539bb9

Browse files
committed
Add classSymbolAfterErasure
1 parent aa42e42 commit 3539bb9

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
@@ -259,7 +259,11 @@ object Types {
259259
/** True if this type is an instance of the given `cls` or an instance of
260260
* a non-bottom subclass of `cls`.
261261
*/
262-
final def derivesFrom(cls: Symbol, afterErasure: Boolean = false)(using Context): Boolean = {
262+
final def derivesFrom(cls: Symbol, isErased: Boolean = false)(using Context): Boolean = {
263+
def isLowerBottomType(tp: Type) =
264+
(if isErased then tp.isBottomTypeAfterErasure else tp.isBottomType)
265+
&& (tp.hasClassSymbol(defn.NothingClass)
266+
|| cls != defn.NothingClass && !cls.isValueClass)
263267
def loop(tp: Type): Boolean = tp match {
264268
case tp: TypeRef =>
265269
val sym = tp.symbol
@@ -276,10 +280,6 @@ object Types {
276280
// If the type is `T | Null` or `T | Nothing`, the class is != Nothing,
277281
// and `T` derivesFrom the class, then the OrType derivesFrom the class.
278282
// Otherwise, we need to check both sides derivesFrom the class.
279-
def isLowerBottomType(tp: Type) =
280-
(if afterErasure then t.isBottomTypeAfterErasure else t.isBottomType)
281-
&& (tp.hasClassSymbol(defn.NothingClass)
282-
|| cls != defn.NothingClass && !cls.isValueClass)
283283
if isLowerBottomType(tp.tp1) then
284284
loop(tp.tp2)
285285
else if isLowerBottomType(tp.tp2) then
@@ -463,28 +463,45 @@ object Types {
463463
* instance, or NoSymbol if none exists (either because this type is not a
464464
* value type, or because superclasses are ambiguous).
465465
*/
466-
final def classSymbol(using Context): Symbol = this match {
467-
case tp: TypeRef =>
468-
val sym = tp.symbol
469-
if (sym.isClass) sym else tp.superType.classSymbol
470-
case tp: TypeProxy =>
471-
tp.underlying.classSymbol
472-
case tp: ClassInfo =>
473-
tp.cls
474-
case AndType(l, r) =>
475-
val lsym = l.classSymbol
476-
val rsym = r.classSymbol
477-
if (lsym isSubClass rsym) lsym
478-
else if (rsym isSubClass lsym) rsym
479-
else NoSymbol
480-
case tp: OrType =>
481-
tp.join.classSymbol
482-
case _: JavaArrayType =>
483-
defn.ArrayClass
484-
case _ =>
485-
NoSymbol
486-
}
466+
final def classSymbol(using Context): Symbol = classSymbolWith(false)
467+
final def classSymbolAfterErasure(using Context): Symbol = classSymbolWith(true)
468+
469+
final private def classSymbolWith(isErased: Boolean)(using Context): Symbol = {
470+
def loop(tp:Type): Symbol = tp match {
471+
case tp: TypeRef =>
472+
val sym = tp.symbol
473+
if (sym.isClass) sym else loop(tp.superType)
474+
case tp: TypeProxy =>
475+
loop(tp.underlying)
476+
case tp: ClassInfo =>
477+
tp.cls
478+
case AndType(l, r) =>
479+
val lsym = loop(l)
480+
val rsym = loop(r)
481+
if (lsym isSubClass rsym) lsym
482+
else if (rsym isSubClass lsym) rsym
483+
else NoSymbol
484+
case tp: OrType =>
485+
if tp.tp1.hasClassSymbol(defn.NothingClass) then
486+
loop(tp.tp2)
487+
else if tp.tp2.hasClassSymbol(defn.NothingClass) then
488+
loop(tp.tp1)
489+
else
490+
val tp1Null = tp.tp1.hasClassSymbol(defn.NullClass)
491+
val tp2Null = tp.tp2.hasClassSymbol(defn.NullClass)
492+
if isErased && (tp1Null || tp2Null) then
493+
val otherSide = if tp1Null then loop(tp.tp2) else loop(tp.tp1)
494+
if otherSide.isValueClass then defn.AnyClass else otherSide
495+
else
496+
loop(tp.join)
497+
case _: JavaArrayType =>
498+
defn.ArrayClass
499+
case _ =>
500+
NoSymbol
501+
}
487502

503+
loop(this)
504+
}
488505
/** The least (wrt <:<) set of symbols satisfying the `include` predicate of which this type is a subtype
489506
*/
490507
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)