Skip to content

Commit cfe683f

Browse files
committed
Merge remote-tracking branch 'staging/UnsafeNullsSimplified' into UnsafeNulls
2 parents 6b642df + 21168a4 commit cfe683f

21 files changed

+123
-214
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +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 Phases._, NullOpsDecorator._
13+
import Phases._
1414
import collection.{immutable, mutable}
1515
import util.{Property, SourceFile, NoSource}
1616
import NameKinds.{TempResultName, OuterSelectName}

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

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import Decorators._
88
import collection.mutable
99
import config.SourceVersion.future
1010
import config.Feature.sourceVersion
11-
import typer.Nullables
1211

1312
/** Realizability status */
1413
object CheckRealizable {
@@ -149,14 +148,10 @@ class CheckRealizable(using Context) {
149148
*/
150149
private def boundsRealizability(tp: Type) = {
151150

152-
// In unsafe nulls, we use the old subtyping to check the bounds
153-
def isSub(tp1: Type, tp2: Type): Boolean =
154-
Nullables.useUnsafeNullsSubType { tp1 <:< tp2 }
155-
156151
val memberProblems = withMode(Mode.CheckBounds) {
157152
for {
158153
mbr <- tp.nonClassTypeMembers
159-
if !isSub(mbr.info.loBound, mbr.info.hiBound)
154+
if !(mbr.info.loBound <:< mbr.info.hiBound)
160155
}
161156
yield new HasProblemBounds(mbr.name, mbr.info)
162157
}
@@ -166,7 +161,7 @@ class CheckRealizable(using Context) {
166161
name <- refinedNames(tp)
167162
if (name.isTypeName)
168163
mbr <- tp.member(name).alternatives
169-
if !isSub(mbr.info.loBound, mbr.info.hiBound)
164+
if !(mbr.info.loBound <:< mbr.info.hiBound)
170165
}
171166
yield
172167
new HasProblemBounds(name, mbr.info)
@@ -177,7 +172,7 @@ class CheckRealizable(using Context) {
177172
new HasProblemBase(base1, base2) :: Nil
178173
case base =>
179174
base.argInfos.collect {
180-
case bounds @ TypeBounds(lo, hi) if !isSub(lo, hi) =>
175+
case bounds @ TypeBounds(lo, hi) if !(lo <:< hi) =>
181176
new HasProblemBaseArg(base, bounds)
182177
}
183178
}
@@ -216,4 +211,4 @@ class CheckRealizable(using Context) {
216211
else
217212
Realizable
218213
}
219-
}
214+
}

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

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,6 @@ object NullOpsDecorator:
4949
val stripped = self.stripNull
5050
stripped ne self
5151
}
52-
53-
/** Types nullable after erasure are:
54-
* - Null itself
55-
* - Any type that is a subtype of `Object`, excluding `Nothing`
56-
*/
57-
def isNullableAfterErasure(using Context): Boolean =
58-
self.isNullType
59-
|| !self.isNothingType
60-
&& self.stripNull.derivesFrom(defn.ObjectClass)
6152
end extension
6253

6354
import ast.tpd._

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,8 @@ object SymDenotations {
797797

798798
/** Is this symbol a class of which `null` is a value? */
799799
final def isNullableClass(using Context): Boolean =
800-
if (ctx.explicitNulls && !ctx.phase.erasedTypes) symbol == defn.NullClass || symbol == defn.AnyClass
800+
if ctx.mode.is(Mode.SafeNulls) && !ctx.phase.erasedTypes
801+
then symbol == defn.NullClass || symbol == defn.AnyClass
801802
else isNullableClassAfterErasure
802803

803804
/** Is this symbol a class of which `null` is a value after erasure?
@@ -1823,12 +1824,14 @@ object SymDenotations {
18231824
)
18241825

18251826
final override def isSubClass(base: Symbol)(using Context): Boolean =
1826-
derivesFrom(base) ||
1827-
base.isClass && (
1828-
(symbol eq defn.NothingClass) ||
1829-
(!ctx.explicitNulls || ctx.phase.erasedTypes)
1830-
&& (symbol eq defn.NullClass)
1831-
&& (base ne defn.NothingClass))
1827+
derivesFrom(base)
1828+
|| base.isClass
1829+
&& (
1830+
(symbol eq defn.NothingClass)
1831+
|| (symbol eq defn.NullClass)
1832+
&& (!ctx.mode.is(Mode.SafeNulls) || ctx.phase.erasedTypes)
1833+
&& (base ne defn.NothingClass)
1834+
)
18321835

18331836
/** Is it possible that a class inherits both `this` and `that`?
18341837
*

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

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ import Decorators._
1010
import util.Stats._
1111
import Names._
1212
import NameOps._
13-
import NullOpsDecorator._
1413
import Flags.Module
1514
import Variances.variancesConform
16-
import typer.Nullables
1715
import dotty.tools.dotc.config.Config
1816

1917
object TypeApplications {
@@ -403,45 +401,41 @@ class TypeApplications(val self: Type) extends AnyVal {
403401
}
404402

405403
/** Translate a type of the form From[T] to either To[T] or To[? <: T] (if `wildcardArg` is set).
406-
* Ff `withOrNull` is true, the elemTp will become nullable.
407404
* Keep other types as they are.
408405
* `from` and `to` must be static classes, both with one type parameter, and the same variance.
409406
* Do the same for by name types => From[T] and => To[T]
410407
*/
411-
def translateParameterized(from: ClassSymbol, to: ClassSymbol, wildcardArg: Boolean = false, withOrNull: Boolean = false)(using Context): Type = self match {
408+
def translateParameterized(from: ClassSymbol, to: ClassSymbol, wildcardArg: Boolean = false)(using Context): Type = self match
412409
case self @ ExprType(tp) =>
413410
self.derivedExprType(tp.translateParameterized(from, to))
414-
case OrNull(tp) =>
415-
tp.translateParameterized(from, to, wildcardArg, withOrNull)
416411
case _ =>
417412
if self.derivesFrom(from) then
418413
def elemType(tp: Type): Type = tp.widenDealias match
414+
case tp: OrType =>
415+
if tp.tp1.isBottomType then elemType(tp.tp2)
416+
else if tp.tp2.isBottomType then elemType(tp.tp1)
417+
else tp.derivedOrType(elemType(tp.tp1), elemType(tp.tp2))
419418
case tp: AndType => tp.derivedAndType(elemType(tp.tp1), elemType(tp.tp2))
420419
case _ => tp.baseType(from).argInfos.headOption.getOrElse(defn.NothingType)
421420
val arg = elemType(self)
422-
val arg1 = if withOrNull && arg.isNullableAfterErasure then OrNull(arg) else arg
423-
val arg2 = if (wildcardArg) TypeBounds.upper(arg1) else arg1
424-
to.typeRef.appliedTo(arg2)
421+
val arg1 = if (wildcardArg) TypeBounds.upper(arg) else arg
422+
to.typeRef.appliedTo(arg1)
425423
else self
426-
}
427424

428425
/** If this is a repeated parameter `*T`, translate it to either `Seq[T]` or
429426
* `Array[? <: T]` depending on the value of `toArray`.
430427
* Additionally, if `translateWildcard` is true, a wildcard type
431-
* will be translated to `*<?>`, and if `withOrNull` is true, the elemTp
432-
* will become nullable.
433-
* Other types are kept as-is.
428+
* will be translated to `*<?>`. Other types are kept as-is.
434429
*/
435-
def translateFromRepeated(toArray: Boolean, translateWildcard: Boolean = false, withOrNull: Boolean = false)(using Context): Type =
430+
def translateFromRepeated(toArray: Boolean, translateWildcard: Boolean = false)(using Context): Type =
436431
val seqClass = if (toArray) defn.ArrayClass else defn.SeqClass
437-
val tp = if translateWildcard && self.isInstanceOf[WildcardType] then
432+
if translateWildcard && self.isInstanceOf[WildcardType] then
438433
seqClass.typeRef.appliedTo(WildcardType)
439434
else if self.isRepeatedParam then
440435
// We want `Array[? <: T]` because arrays aren't covariant until after
441436
// erasure. See `tests/pos/i5140`.
442-
translateParameterized(defn.RepeatedParamClass, seqClass, wildcardArg = toArray, withOrNull = withOrNull)
437+
translateParameterized(defn.RepeatedParamClass, seqClass, wildcardArg = toArray)
443438
else self
444-
if withOrNull then OrNull(tp) else tp
445439

446440
/** Translate a `From[T]` into a `*T`. */
447441
def translateToRepeated(from: ClassSymbol)(using Context): Type =

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

Lines changed: 31 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -485,42 +485,38 @@ object Types {
485485
* instance, or NoSymbol if none exists (either because this type is not a
486486
* value type, or because superclasses are ambiguous).
487487
*/
488-
final def classSymbol(using Context): Symbol = {
489-
def loop(tp:Type): Symbol = tp match {
490-
case tp: TypeRef =>
491-
val sym = tp.symbol
492-
if (sym.isClass) sym else loop(tp.superType)
493-
case tp: TypeProxy =>
494-
loop(tp.underlying)
495-
case tp: ClassInfo =>
496-
tp.cls
497-
case AndType(l, r) =>
498-
val lsym = loop(l)
499-
val rsym = loop(r)
500-
if (lsym isSubClass rsym) lsym
501-
else if (rsym isSubClass lsym) rsym
502-
else NoSymbol
503-
case tp: OrType =>
504-
if tp.tp1.hasClassSymbol(defn.NothingClass) then
505-
loop(tp.tp2)
506-
else if tp.tp2.hasClassSymbol(defn.NothingClass) then
507-
loop(tp.tp1)
488+
final def classSymbol(using Context): Symbol = this match
489+
case tp: TypeRef =>
490+
val sym = tp.symbol
491+
if (sym.isClass) sym else tp.superType.classSymbol
492+
case tp: TypeProxy =>
493+
tp.underlying.classSymbol
494+
case tp: ClassInfo =>
495+
tp.cls
496+
case AndType(l, r) =>
497+
val lsym = l.classSymbol
498+
val rsym = r.classSymbol
499+
if (lsym isSubClass rsym) lsym
500+
else if (rsym isSubClass lsym) rsym
501+
else NoSymbol
502+
case tp: OrType =>
503+
if tp.tp1.hasClassSymbol(defn.NothingClass) then
504+
tp.tp2.classSymbol
505+
else if tp.tp2.hasClassSymbol(defn.NothingClass) then
506+
tp.tp1.classSymbol
507+
else
508+
def tp1Null = tp.tp1.hasClassSymbol(defn.NullClass)
509+
def tp2Null = tp.tp2.hasClassSymbol(defn.NullClass)
510+
if ctx.erasedTypes && (tp1Null || tp2Null) then
511+
val otherSide = if tp1Null then tp.tp2.classSymbol else tp.tp1.classSymbol
512+
if otherSide.isValueClass then defn.AnyClass else otherSide
508513
else
509-
val tp1Null = tp.tp1.hasClassSymbol(defn.NullClass)
510-
val tp2Null = tp.tp2.hasClassSymbol(defn.NullClass)
511-
if ctx.erasedTypes && (tp1Null || tp2Null) then
512-
val otherSide = if tp1Null then loop(tp.tp2) else loop(tp.tp1)
513-
if otherSide.isValueClass then defn.AnyClass else otherSide
514-
else
515-
loop(tp.join)
516-
case _: JavaArrayType =>
517-
defn.ArrayClass
518-
case _ =>
519-
NoSymbol
520-
}
514+
tp.join.classSymbol
515+
case _: JavaArrayType =>
516+
defn.ArrayClass
517+
case _ =>
518+
NoSymbol
521519

522-
loop(this)
523-
}
524520
/** The least (wrt <:<) set of symbols satisfying the `include` predicate of which this type is a subtype
525521
*/
526522
final def parentSymbols(include: Symbol => Boolean)(using Context): List[Symbol] = this match {
@@ -1096,7 +1092,7 @@ object Types {
10961092
*/
10971093
def matches(that: Type)(using Context): Boolean = {
10981094
record("matches")
1099-
withMode(Mode.UnsafeNullsSubType)(
1095+
withoutMode(Mode.SafeNulls)(
11001096
TypeComparer.matchesType(this, that, relaxed = !ctx.phase.erasedTypes))
11011097
}
11021098

compiler/src/dotty/tools/dotc/interactive/Completion.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import dotty.tools.dotc.core.StdNames.{nme, tpnme}
1919
import dotty.tools.dotc.core.TypeComparer
2020
import dotty.tools.dotc.core.TypeError
2121
import dotty.tools.dotc.core.Types.{ExprType, MethodType, NameFilter, NamedType, NoType, PolyType, Type}
22-
import dotty.tools.dotc.typer.Nullables
2322
import dotty.tools.dotc.printing.Texts._
2423
import dotty.tools.dotc.util.{NameTransformer, NoSourcePosition, SourcePosition}
2524

@@ -253,7 +252,7 @@ object Completion {
253252
}
254253

255254
// 2. The extension method is a member of some given instance that is visible at the point of the reference.
256-
val givensInScope = ctx.implicits.eligible(defn.AnyType, Nullables.unsafeNullsEnabled).map(_.implicitRef.underlyingRef)
255+
val givensInScope = ctx.implicits.eligible(defn.AnyType).map(_.implicitRef.underlyingRef)
257256
val extMethodsFromGivensInScope = extractDefinedExtensionMethods(givensInScope)
258257

259258
// 3. The reference is of the form r.m and the extension method is defined in the implicit scope of the type of r.

compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import Decorators._
1414
import Denotations._, SymDenotations._
1515
import TypeErasure.erasure
1616
import DenotTransformers._
17-
import NullOpsDecorator._
1817

1918
object ElimRepeated {
2019
val name: String = "elimRepeated"

compiler/src/dotty/tools/dotc/transform/PostTyper.scala

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import core._
77
import dotty.tools.dotc.typer.Checking
88
import dotty.tools.dotc.typer.Inliner
99
import dotty.tools.dotc.typer.VarianceChecker
10-
import dotty.tools.dotc.typer.Nullables
1110
import Types._, Contexts._, Names._, Flags._, DenotTransformers._, Phases._
1211
import SymDenotations._, StdNames._, Annotations._, Trees._, Scopes._
1312
import Decorators._
@@ -314,9 +313,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
314313
args.foreach(checkInferredWellFormed)
315314
if (fn.symbol != defn.ChildAnnot.primaryConstructor)
316315
// Make an exception for ChildAnnot, which should really have AnyKind bounds
317-
Nullables.useUnsafeNullsSubType {
318-
Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType])
319-
}
316+
Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType])
320317
fn match {
321318
case sel: Select =>
322319
val args1 = transform(args)
@@ -378,9 +375,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
378375
else if (tree.tpt.symbol == defn.orType)
379376
() // nothing to do
380377
else
381-
Nullables.useUnsafeNullsSubType {
382-
Checking.checkAppliedType(tree)
383-
}
378+
Checking.checkAppliedType(tree)
384379
super.transform(tree)
385380
case SingletonTypeTree(ref) =>
386381
Checking.checkRealizable(ref.tpe, ref.srcPos)
@@ -442,4 +437,4 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
442437
private def normalizeErasedRhs(rhs: Tree, sym: Symbol)(using Context) =
443438
if (sym.isEffectivelyErased) dropInlines.transform(rhs) else rhs
444439
}
445-
}
440+
}

compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
210210
// Second constructor of ioob that takes a String argument
211211
def filterStringConstructor(s: Symbol): Boolean = s.info match {
212212
case m: MethodType if s.isConstructor && m.paramInfos.size == 1 =>
213-
val head = m.paramInfos.head
214-
val pinfo = head.stripNull
215-
pinfo == defn.StringType
213+
m.paramInfos.head.stripNull == defn.StringType
216214
case _ => false
217215
}
218216
val constructor = ioob.typeSymbol.info.decls.find(filterStringConstructor _).asTerm

compiler/src/dotty/tools/dotc/transform/TreeChecker.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import core.NameKinds.{DocArtifactName, OuterSelectName}
1313
import core.Decorators._
1414
import core.Phases._
1515
import core.Mode
16-
import core.NullOpsDecorator._
1716
import typer._
1817
import typer.ErrorReporting._
1918
import reporting._

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,7 @@ trait Checking {
818818
def checkImplicitConversionUseOK(tree: Tree)(using Context): Unit =
819819
val tree1 = if Nullables.unsafeNullsEnabled then
820820
// If unsafeNulls is enabled, a cast and a closure could be added to the original tree
821+
// !!! TODO: We need a test for that. Right now everything passes even if this case is commented out.
821822
stripCast(closureBody(tree))
822823
else tree
823824
val sym = tree1.symbol

compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ object ErrorReporting {
161161
case fail: Implicits.NoMatchingImplicits => // do nothing
162162
case _ => attempts += ((failure.tree, ""))
163163
if foundWithoutNull then
164+
// !!! TODO: The need a test that tests this error message with a check file
164165
i""".
165166
|Since explicit-nulls is enabled, the selection is rejected because
166167
|${qualType.widen} could be null at runtime.

0 commit comments

Comments
 (0)