Skip to content

Nullability Analysis without NotNull #7556

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 37 commits into from
Nov 15, 2019
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6e271a7
Thread context through typedStats
odersky Nov 8, 2019
435024a
Nullability prototype
odersky Nov 8, 2019
06f72db
Add extractor for comparisons of null and a path
odersky Nov 8, 2019
d904a3c
Generalize expression purity
odersky Nov 8, 2019
6a7c740
Simplify more if-expressions in FirstTransform
odersky Nov 8, 2019
e96e027
Maintain constant types of applications in TimeTravellingTreeCopier
odersky Nov 8, 2019
d1981f8
Constant fold null tests
odersky Nov 8, 2019
16cc066
Fix handling of negation for nullability
odersky Nov 8, 2019
a38574c
Skip also Inlined nodes with skipBlock
odersky Nov 8, 2019
1910c87
Always compute nullable info unless ersedTypes
odersky Nov 8, 2019
a691e96
Recompute nullability info when inlining
odersky Nov 8, 2019
691996a
Tweak assert so that nullability info is propagated correctly
odersky Nov 8, 2019
94d05a4
Exclude nullable.scala from pickling and fromTasy tests
odersky Nov 8, 2019
27c92a9
Disable sjsJUnitTests
odersky Nov 8, 2019
cb6ff0c
Move null comparison extractors into Nullables
odersky Nov 9, 2019
0d2a0f0
Track nullability in pattern matches
odersky Nov 9, 2019
b20cae8
Polishings
odersky Nov 9, 2019
2d5007c
Allow retracting a not null status
odersky Nov 10, 2019
10896a1
Nullables refactorings
odersky Nov 10, 2019
5b46483
Account for side effects in conditions
odersky Nov 10, 2019
5cb0873
Track assignments for nullability
odersky Nov 10, 2019
6d5ed25
Allow local variables to be tracked for nullability
odersky Nov 10, 2019
74d5b1e
Drop debug output
odersky Nov 10, 2019
f1e2863
Constant fold null comparisons only under -Yexplicit-nulls
odersky Nov 10, 2019
ed00d96
Handle while expressions correctly for nullability
odersky Nov 11, 2019
926b08e
Don't widen T | Null in widenUnion
odersky Nov 11, 2019
947c49e
Introduce NotNull type
odersky Nov 12, 2019
62b756b
Simplify in widenRHS
odersky Nov 12, 2019
e6746e5
Special treatment of NotNull in TypeComparer
odersky Nov 12, 2019
f32f22a
Temporary hack to support testing
odersky Nov 12, 2019
e79dc49
Test case
odersky Nov 12, 2019
e56743b
Blacklist notNull as from Tasty test
odersky Nov 12, 2019
84c0fdf
Revert "Disable sjsJUnitTests"
odersky Nov 13, 2019
b32e30a
Address review suggestions
odersky Nov 13, 2019
1726660
Add notNull member to Any
odersky Nov 14, 2019
434204f
Use a definition of `def notNull` that does not require `NotNull`.
sjrd Nov 14, 2019
016f471
Revert all the changes that added NonNull.
sjrd Nov 14, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions compiler/src/dotty/tools/dotc/CompilationUnit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import util.SourceFile
import ast.{tpd, untpd}
import tpd.{Tree, TreeTraverser}
import typer.PrepareInlineable.InlineAccessors
import typer.Nullables
import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.SymDenotations.ClassDenotation
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.transform.SymUtils._
import util.{NoSource, SourceFile}
import util.Spans.Span
import core.Decorators._

class CompilationUnit protected (val source: SourceFile) {
Expand Down Expand Up @@ -42,6 +44,16 @@ class CompilationUnit protected (val source: SourceFile) {
suspended = true
ctx.run.suspendedUnits += this
throw CompilationUnit.SuspendException()

private var myAssignmentSpans: Map[Int, List[Span]] = null

/** A map from (name-) offsets of all local variables in this compilation unit
* that can be tracked for being not null to the list of spans of assignments
* to these variables.
*/
def assignmentSpans(given Context): Map[Int, List[Span]] =
if myAssignmentSpans == null then myAssignmentSpans = Nullables.assignmentSpans
myAssignmentSpans
}

object CompilationUnit {
Expand Down
10 changes: 9 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
/** If this is a block, its expression part */
def stripBlock(tree: Tree): Tree = unsplice(tree) match {
case Block(_, expr) => stripBlock(expr)
case Inlined(_, _, expr) => stripBlock(expr)
case _ => tree
}

def stripInlined(tree: Tree): Tree = unsplice(tree) match {
case Inlined(_, _, expr) => stripInlined(expr)
case _ => tree
}

Expand Down Expand Up @@ -391,7 +397,9 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
if (fn.symbol.is(Erased) || fn.symbol == defn.InternalQuoted_typeQuote) Pure else exprPurity(fn)
case Apply(fn, args) =>
def isKnownPureOp(sym: Symbol) =
sym.owner.isPrimitiveValueClass || sym.owner == defn.StringClass
sym.owner.isPrimitiveValueClass
|| sym.owner == defn.StringClass
|| defn.pureMethods.contains(sym)
if (tree.tpe.isInstanceOf[ConstantType] && isKnownPureOp(tree.symbol) // A constant expression with pure arguments is pure.
|| (fn.symbol.isStableMember && !fn.symbol.is(Lazy))
|| fn.symbol.isPrimaryConstructor && fn.symbol.owner.isNoInitsClass) // TODO: include in isStable?
Expand Down
14 changes: 11 additions & 3 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -711,11 +711,19 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {

class TimeTravellingTreeCopier extends TypedTreeCopier {
override def Apply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): Apply =
ta.assignType(untpdCpy.Apply(tree)(fun, args), fun, args)
tree match
case tree: Apply
if (tree.fun eq fun) && (tree.args eq args)
&& tree.tpe.isInstanceOf[ConstantType]
&& isPureExpr(tree) => tree
case _ =>
ta.assignType(untpdCpy.Apply(tree)(fun, args), fun, args)
// Note: Reassigning the original type if `fun` and `args` have the same types as before
// does not work here: The computed type depends on the widened function type, not
// the function type itself. A treetransform may keep the function type the
// does not work here in general: The computed type depends on the widened function type, not
// the function type itself. A tree transform may keep the function type the
// same but its widened type might change.
// However, we keep constant types of pure expressions. This uses the underlying assumptions
// that pure functions yielding a constant will not change in later phases.

override def TypeApply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): TypeApply =
ta.assignType(untpdCpy.TypeApply(tree)(fun, args), fun, args)
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ class ScalaSettings extends Settings.SettingGroup {

// Extremely experimental language features
val YnoKindPolymorphism: Setting[Boolean] = BooleanSetting("-Yno-kind-polymorphism", "Enable kind polymorphism (see https://dotty.epfl.ch/docs/reference/kind-polymorphism.html). Potentially unsound.")
val YexplicitNulls: Setting[Boolean] = BooleanSetting("-Yexplicit-nulls", "Make reference types non-nullable. Nullable types can be expressed with unions: e.g. String|Null.")

/** Area-specific debug output */
val YexplainLowlevel: Setting[Boolean] = BooleanSetting("-Yexplain-lowlevel", "When explaining type errors, show types at a lower level.")
Expand Down
28 changes: 25 additions & 3 deletions compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import ast.Trees._
import ast.untpd
import Flags.GivenOrImplicit
import util.{FreshNameCreator, NoSource, SimpleIdentityMap, SourceFile}
import typer.{Implicits, ImportInfo, Inliner, NamerContextOps, SearchHistory, SearchRoot, TypeAssigner, Typer}
import typer.{Implicits, ImportInfo, Inliner, NamerContextOps, SearchHistory, SearchRoot, TypeAssigner, Typer, Nullables}
import Nullables.{NotNullInfo, given}
import Implicits.ContextualImplicits
import config.Settings._
import config.Config
Expand Down Expand Up @@ -47,7 +48,11 @@ object Contexts {
private val (compilationUnitLoc, store6) = store5.newLocation[CompilationUnit]()
private val (runLoc, store7) = store6.newLocation[Run]()
private val (profilerLoc, store8) = store7.newLocation[Profiler]()
private val initialStore = store8
private val (notNullInfosLoc, store9) = store8.newLocation[List[NotNullInfo]]()
private val initialStore = store9

/** The current context */
def curCtx(given ctx: Context): Context = ctx

/** A context is passed basically everywhere in dotc.
* This is convenient but carries the risk of captured contexts in
Expand Down Expand Up @@ -207,6 +212,9 @@ object Contexts {
/** The current compiler-run profiler */
def profiler: Profiler = store(profilerLoc)

/** The paths currently known to be not null */
def notNullInfos = store(notNullInfosLoc)

/** The new implicit references that are introduced by this scope */
protected var implicitsCache: ContextualImplicits = null
def implicits: ContextualImplicits = {
Expand Down Expand Up @@ -556,6 +564,7 @@ object Contexts {
def setRun(run: Run): this.type = updateStore(runLoc, run)
def setProfiler(profiler: Profiler): this.type = updateStore(profilerLoc, profiler)
def setFreshNames(freshNames: FreshNameCreator): this.type = updateStore(freshNamesLoc, freshNames)
def setNotNullInfos(notNullInfos: List[NotNullInfo]): this.type = updateStore(notNullInfosLoc, notNullInfos)

def setProperty[T](key: Key[T], value: T): this.type =
setMoreProperties(moreProperties.updated(key, value))
Expand Down Expand Up @@ -587,6 +596,17 @@ object Contexts {
def setDebug: this.type = setSetting(base.settings.Ydebug, true)
}

given (c: Context)
def addNotNullInfo(info: NotNullInfo) =
c.withNotNullInfos(c.notNullInfos.extendWith(info))

def addNotNullRefs(refs: Set[TermRef]) =
c.addNotNullInfo(NotNullInfo(refs, Set()))

def withNotNullInfos(infos: List[NotNullInfo]): Context =
if c.notNullInfos eq infos then c else c.fresh.setNotNullInfos(infos)

// TODO: Fix issue when converting ModeChanges and FreshModeChanges to extension givens
implicit class ModeChanges(val c: Context) extends AnyVal {
final def withModeBits(mode: Mode): Context =
if (mode != c.mode) c.fresh.setMode(mode) else c
Expand Down Expand Up @@ -615,7 +635,9 @@ object Contexts {
typeAssigner = TypeAssigner
moreProperties = Map.empty
source = NoSource
store = initialStore.updated(settingsStateLoc, settingsGroup.defaultState)
store = initialStore
.updated(settingsStateLoc, settingsGroup.defaultState)
.updated(notNullInfosLoc, Nil)
typeComparer = new TypeComparer(this)
searchHistory = new SearchRoot
gadt = EmptyGadtConstraint
Expand Down
28 changes: 23 additions & 5 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -249,10 +249,12 @@ class Definitions {
*/
@tu lazy val AnyClass: ClassSymbol = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.Any, Abstract, Nil), ensureCtor = false)
def AnyType: TypeRef = AnyClass.typeRef
@tu lazy val AnyValClass: ClassSymbol = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.AnyVal, Abstract, List(AnyClass.typeRef)))
@tu lazy val AnyValClass: ClassSymbol = completeClass(
enterCompleteClassSymbol(ScalaPackageClass, tpnme.AnyVal, Abstract,
List(AnyClass.typeRef, NotNullClass.typeRef)))
def AnyValType: TypeRef = AnyValClass.typeRef

@tu lazy val Any_== : TermSymbol = enterMethod(AnyClass, nme.EQ, methOfAny(BooleanType), Final)
@tu lazy val Any_== : TermSymbol = enterMethod(AnyClass, nme.EQ, methOfAny(BooleanType), Final)
@tu lazy val Any_!= : TermSymbol = enterMethod(AnyClass, nme.NE, methOfAny(BooleanType), Final)
@tu lazy val Any_equals: TermSymbol = enterMethod(AnyClass, nme.equals_, methOfAny(BooleanType))
@tu lazy val Any_hashCode: TermSymbol = enterMethod(AnyClass, nme.hashCode_, MethodType(Nil, IntType))
Expand All @@ -261,6 +263,8 @@ class Definitions {
@tu lazy val Any_isInstanceOf: TermSymbol = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOf_, _ => BooleanType, Final)
@tu lazy val Any_asInstanceOf: TermSymbol = enterT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, _.paramRefs(0), Final)
@tu lazy val Any_typeTest: TermSymbol = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOfPM, _ => BooleanType, Final | Synthetic | Artifact)
@tu lazy val Any_notNull: TermSymbol = newSymbol(AnyClass, nme.NOT_NULL, Method | Final | Erased | Artifact| StableRealizable,
AndType(AnyClass.thisType, NotNullClass.typeRef)).entered
@tu lazy val Any_typeCast: TermSymbol = enterT1ParameterlessMethod(AnyClass, nme.asInstanceOfPM, _.paramRefs(0), Final | Synthetic | Artifact | StableRealizable)
// generated by pattern matcher, eliminated by erasure

Expand All @@ -272,13 +276,16 @@ class Definitions {
Final,
bounds = TypeBounds.lower(AnyClass.thisType))

def AnyMethods: List[TermSymbol] = List(Any_==, Any_!=, Any_equals, Any_hashCode,
Any_toString, Any_##, Any_getClass, Any_isInstanceOf, Any_asInstanceOf, Any_typeTest, Any_typeCast)
private def AnyMethods: List[TermSymbol] = List(Any_==, Any_!=, Any_equals, Any_hashCode,
Any_toString, Any_##, Any_getClass, Any_isInstanceOf, Any_asInstanceOf, Any_typeTest, Any_typeCast, Any_notNull)

def isAny_notNull(sym: Symbol)(given Context) = sym.name == nme.NOT_NULL && sym == Any_notNull

@tu lazy val ObjectClass: ClassSymbol = {
val cls = ctx.requiredClass("java.lang.Object")
assert(!cls.isCompleted, "race for completing java.lang.Object")
cls.info = ClassInfo(cls.owner.thisType, cls, AnyClass.typeRef :: Nil, newScope)
cls.info = ClassInfo(cls.owner.thisType, cls,
List(AnyClass.typeRef, NotNullClass.typeRef), newScope)
cls.setFlag(NoInits | JavaDefined)

// The companion object doesn't really exist, so it needs to be marked as
Expand Down Expand Up @@ -310,6 +317,10 @@ class Definitions {
def ObjectMethods: List[TermSymbol] = List(Object_eq, Object_ne, Object_synchronized, Object_clone,
Object_finalize, Object_notify, Object_notifyAll, Object_wait, Object_waitL, Object_waitLI)

/** Methods in Object and Any that do not have a side effect */
@tu lazy val pureMethods: List[TermSymbol] = List(Any_==, Any_!=, Any_equals, Any_hashCode,
Any_toString, Any_##, Any_getClass, Any_isInstanceOf, Any_typeTest, Object_eq, Object_ne)

@tu lazy val AnyKindClass: ClassSymbol = {
val cls = ctx.newCompleteClassSymbol(ScalaPackageClass, tpnme.AnyKind, AbstractFinal | Permanent, Nil)
if (!ctx.settings.YnoKindPolymorphism.value)
Expand Down Expand Up @@ -399,6 +410,11 @@ class Definitions {
List(AnyClass.typeRef), EmptyScope)
@tu lazy val SingletonType: TypeRef = SingletonClass.typeRef

@tu lazy val NotNullClass: ClassSymbol =
enterCompleteClassSymbol(
ScalaPackageClass, tpnme.NotNull, PureInterfaceCreationFlags,
List(AnyClass.typeRef), EmptyScope)

@tu lazy val CollectionSeqType: TypeRef = ctx.requiredClassRef("scala.collection.Seq")
@tu lazy val SeqType: TypeRef = ctx.requiredClassRef("scala.collection.immutable.Seq")
def SeqClass(given Context): ClassSymbol = SeqType.symbol.asClass
Expand Down Expand Up @@ -1286,6 +1302,7 @@ class Definitions {
.updated(AnyClass, ObjectClass)
.updated(AnyValClass, ObjectClass)
.updated(SingletonClass, ObjectClass)
.updated(NotNullClass, ObjectClass)
.updated(TupleClass, ObjectClass)
.updated(NonEmptyTupleClass, ProductClass)

Expand All @@ -1302,6 +1319,7 @@ class Definitions {
ByNameParamClass2x,
AnyValClass,
NullClass,
NotNullClass,
NothingClass,
SingletonClass)

Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ object StdNames {
final val ExprApi: N = "ExprApi"
final val Mirror: N = "Mirror"
final val Nothing: N = "Nothing"
final val NotNull: N = "NotNull"
final val Null: N = "Null"
final val Object: N = "Object"
final val Product: N = "Product"
Expand Down Expand Up @@ -261,6 +262,7 @@ object StdNames {
val MIRROR_PREFIX: N = "$m."
val MIRROR_SHORT: N = "$m"
val MIRROR_UNTYPED: N = "$m$untyped"
val NOT_NULL: N = "$nn"
val REIFY_FREE_PREFIX: N = "free$"
val REIFY_FREE_THIS_SUFFIX: N = "$this"
val REIFY_FREE_VALUE_SUFFIX: N = "$value"
Expand Down
67 changes: 42 additions & 25 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
private var myAnyKindClass: ClassSymbol = null
private var myNothingClass: ClassSymbol = null
private var myNullClass: ClassSymbol = null
private var myNotNullClass: ClassSymbol = null
private var myObjectClass: ClassSymbol = null
private var myAnyType: TypeRef = null
private var myAnyKindType: TypeRef = null
Expand All @@ -83,6 +84,10 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
if (myNullClass == null) myNullClass = defn.NullClass
myNullClass
}
def NotNullClass: ClassSymbol =
if myNotNullClass == null then myNotNullClass = defn.NotNullClass
myNotNullClass

def ObjectClass: ClassSymbol = {
if (myObjectClass == null) myObjectClass = defn.ObjectClass
myObjectClass
Expand Down Expand Up @@ -770,6 +775,15 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
if (tp2a ne tp2) // Follow the alias; this might avoid truncating the search space in the either below
return recur(tp1, tp2a)

if tp11.isRef(NotNullClass)
tp12.widen match
case OrNull(tp12a) if recur(tp12a, tp2) => return true
case _ =>
if tp12.isRef(NotNullClass)
tp11.widen match
case OrNull(tp11a) if recur(tp11a, tp2) => return true
case _ =>

// Rewrite (T111 | T112) & T12 <: T2 to (T111 & T12) <: T2 and (T112 | T12) <: T2
// and analogously for T11 & (T121 | T122) & T12 <: T2
// `&' types to the left of <: are problematic, because
Expand Down Expand Up @@ -1042,7 +1056,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
*/
def isNewSubType(tp1: Type): Boolean =
if (isCovered(tp1) && isCovered(tp2))
//println(s"useless subtype: $tp1 <:< $tp2")
//println(i"useless subtype: $tp1 <:< $tp2")
false
else isSubType(tp1, tp2, approx.addLow)

Expand Down Expand Up @@ -1533,7 +1547,11 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
* combiners are AppliedTypes, RefinedTypes, RecTypes, And/Or-Types or AnnotatedTypes.
*/
private def isCovered(tp: Type): Boolean = tp.dealiasKeepRefiningAnnots.stripTypeVar match {
case tp: TypeRef => tp.symbol.isClass && tp.symbol != NothingClass && tp.symbol != NullClass
case tp: TypeRef =>
tp.symbol.isClass
&& tp.symbol != NothingClass
&& tp.symbol != NullClass
&& tp.symbol != NotNullClass
case tp: AppliedType => isCovered(tp.tycon)
case tp: RefinedOrRecType => isCovered(tp.parent)
case tp: AndType => isCovered(tp.tp1) && isCovered(tp.tp2)
Expand Down Expand Up @@ -1701,28 +1719,27 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
tp11 & tp2 | tp12 & tp2
case _ =>
val tp1a = dropIfSuper(tp1, tp2)
if (tp1a ne tp1) glb(tp1a, tp2)
else {
val tp2a = dropIfSuper(tp2, tp1)
if (tp2a ne tp2) glb(tp1, tp2a)
else tp1 match {
case tp1: ConstantType =>
tp2 match {
case tp2: ConstantType =>
// Make use of the fact that the intersection of two constant types
// types which are not subtypes of each other is known to be empty.
// Note: The same does not apply to singleton types in general.
// E.g. we could have a pattern match against `x.type & y.type`
// which might succeed if `x` and `y` happen to be the same ref
// at run time. It would not work to replace that with `Nothing`.
// However, maybe we can still apply the replacement to
// types which are not explicitly written.
NothingType
case _ => andType(tp1, tp2)
}
case _ => andType(tp1, tp2)
}
}
if (tp1a ne tp1) return glb(tp1a, tp2)
val tp2a = dropIfSuper(tp2, tp1)
if (tp2a ne tp2) return glb(tp1, tp2a)
tp1 match
case tp1: ConstantType =>
tp2 match
case tp2: ConstantType =>
// Make use of the fact that the intersection of two constant types
// types which are not subtypes of each other is known to be empty.
// Note: The same does not apply to singleton types in general.
// E.g. we could have a pattern match against `x.type & y.type`
// which might succeed if `x` and `y` happen to be the same ref
// at run time. It would not work to replace that with `Nothing`.
// However, maybe we can still apply the replacement to
// types which are not explicitly written.
return NothingType
case _ =>
case _ =>
if tp1.isRef(NotNullClass) && tp2.isNull then return NothingType
if tp2.isRef(NotNullClass) && tp1.isNull then return NothingType
andType(tp1, tp2)
}
}
}
Expand Down Expand Up @@ -1838,7 +1855,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
else if (!tp2.exists) tp1
else tp.derivedAndType(tp1, tp2)

/** If some (&-operand of) this type is a supertype of `sub` replace it with `NoType`.
/** If some (&-operand of) `tp` is a supertype of `sub` replace it with `NoType`.
*/
private def dropIfSuper(tp: Type, sub: Type): Type =
if (isSubTypeWhenFrozen(sub, tp)) NoType
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/TypeErasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ object TypeErasure {
else if (sym.isAbstractType) TypeAlias(WildcardType)
else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx))
else if (sym.is(Label)) erase.eraseResult(sym.info)(erasureCtx)
else if sym.is(Erased) && defn.isAny_notNull(sym) then NoType // Q: Should we delete all erased symbols that way?
else erase.eraseInfo(tp, sym)(erasureCtx) match {
case einfo: MethodType =>
if (sym.isGetter && einfo.resultType.isRef(defn.UnitClass))
Expand Down
Loading