Skip to content

Commit f6e6385

Browse files
committed
add tests and clean code
1 parent cfe683f commit f6e6385

21 files changed

+229
-81
lines changed

compiler/src/dotty/tools/dotc/Run.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,13 @@ import Periods._
77
import Symbols._
88
import Types._
99
import Scopes._
10-
import StdNames.nme
1110
import Names.Name
1211
import Denotations.Denotation
1312
import typer.Typer
1413
import typer.ImportInfo._
1514
import Decorators._
1615
import io.{AbstractFile, PlainFile, VirtualFile}
1716
import Phases.unfusedPhases
18-
import config.Feature
1917

2018
import util._
2119
import reporting.Reporter

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@ 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._
1413
import collection.{immutable, mutable}
1514
import util.{Property, SourceFile, NoSource}
1615
import NameKinds.{TempResultName, OuterSelectName}
1716
import typer.ConstFold
18-
import typer.Nullables
1917

2018
import scala.annotation.tailrec
2119
import scala.io.Codec
@@ -471,7 +469,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
471469

472470
/** The wrapped array method name for an array of type elemtp */
473471
def wrapArrayMethodName(elemtp: Type)(using Context): TermName = {
474-
val elemCls = atPhase(erasurePhase.next) { elemtp.classSymbol }
472+
val elemCls = elemtp.classSymbol
475473
if (elemCls.isPrimitiveValueClass) nme.wrapXArray(elemCls.name)
476474
else if (elemCls.derivesFrom(defn.ObjectClass) && !elemCls.isNotRuntimeClass) nme.wrapRefArray
477475
else nme.genericWrapArray

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,4 +211,4 @@ class CheckRealizable(using Context) {
211211
else
212212
Realizable
213213
}
214-
}
214+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import Nullables._
2020
import Implicits.ContextualImplicits
2121
import config.Settings._
2222
import config.Config
23-
import config.Feature
2423
import reporting._
2524
import io.{AbstractFile, NoAbstractFile, PlainFile, Path}
2625
import scala.io.Codec
@@ -29,6 +28,7 @@ import printing._
2928
import config.{JavaPlatform, SJSPlatform, Platform, ScalaSettings}
3029
import classfile.ReusableDataReader
3130
import StdNames.nme
31+
3232
import scala.annotation.internal.sharable
3333

3434
import DenotTransformers.DenotTransformer

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1218,7 +1218,8 @@ class Definitions {
12181218
idx == name.length || name(idx).isDigit && digitsOnlyAfter(name, idx + 1)
12191219

12201220
def isBottomClass(cls: Symbol): Boolean =
1221-
if ctx.explicitNulls && !ctx.phase.erasedTypes then cls == NothingClass
1221+
if ctx.mode.is(Mode.SafeNulls) && !ctx.phase.erasedTypes
1222+
then cls == NothingClass
12221223
else isBottomClassAfterErasure(cls)
12231224

12241225
def isBottomClassAfterErasure(cls: Symbol): Boolean = cls == NothingClass || cls == NullClass

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,4 @@ object Mode {
127127
* This mode forces expansion of inline calls in those positions even during typing.
128128
*/
129129
val ForceInline: Mode = newMode(29, "ForceInline")
130-
131-
/** Should we use Unsafe Nulls SubTyping in TypeComparer?
132-
* If this mode is in the Context, `Null` is considered as a
133-
* subtype of all reference type.
134-
*/
135-
val UnsafeNullsSubType: Mode = newMode(30, "UnsafeNullsSubType")
136130
}

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -761,11 +761,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
761761
isSubType(hi1, tp2, approx.addLow) || compareGADT || tryLiftedToThis1
762762
case _ =>
763763
def isNullable(tp: Type): Boolean = tp.widenDealias match {
764-
case tp: TypeRef =>
765-
if ctx.explicitNulls && ctx.mode.is(Mode.UnsafeNullsSubType) then
766-
tp.symbol.isNullableClassAfterErasure
767-
else
768-
tp.symbol.isNullableClass
764+
case tp: TypeRef => tp.symbol.isNullableClass
769765
case tp: RefinedOrRecType => isNullable(tp.parent)
770766
case tp: AppliedType => isNullable(tp.tycon)
771767
case AndType(tp1, tp2) => isNullable(tp1) && isNullable(tp2)

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

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -816,21 +816,16 @@ trait Checking {
816816
* enabled.
817817
*/
818818
def checkImplicitConversionUseOK(tree: Tree)(using Context): Unit =
819-
val tree1 = if Nullables.unsafeNullsEnabled then
820-
// 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.
822-
stripCast(closureBody(tree))
823-
else tree
824-
val sym = tree1.symbol
819+
val sym = tree.symbol
825820
if sym.name == nme.apply
826821
&& sym.owner.derivesFrom(defn.ConversionClass)
827822
&& !sym.info.isErroneous
828823
then
829-
def conv = methPart(tree1) match
824+
def conv = methPart(tree) match
830825
case Select(qual, _) => qual.symbol.orElse(sym.owner)
831826
case _ => sym.owner
832827
checkFeature(nme.implicitConversions,
833-
i"Use of implicit conversion ${conv.showLocated}", NoSymbol, tree1.srcPos)
828+
i"Use of implicit conversion ${conv.showLocated}", NoSymbol, tree.srcPos)
834829

835830
private def infixOKSinceFollowedBy(tree: untpd.Tree): Boolean = tree match {
836831
case _: untpd.Block | _: untpd.Match => true

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,6 @@ 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
165164
i""".
166165
|Since explicit-nulls is enabled, the selection is rejected because
167166
|${qualType.widen} could be null at runtime.

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -797,11 +797,9 @@ class Typer extends Namer
797797
// A sequence argument `xs: _*` can be either a `Seq[T]` or an `Array[_ <: T]`,
798798
// irrespective of whether the method we're calling is a Java or Scala method,
799799
// so the expected type is the union `Seq[T] | Array[_ <: T]`.
800-
// If unsafe nulls is enabled, the expected type is `Seq[T | Null] | Array[_ <: T | Null] | Null`.
801-
val unsafeNulls = Nullables.unsafeNullsEnabled
802800
val ptArg =
803801
// FIXME(#8680): Quoted patterns do not support Array repeated arguments
804-
if (ctx.mode.is(Mode.QuotedPattern))
802+
if ctx.mode.is(Mode.QuotedPattern) then
805803
pt.translateFromRepeated(toArray = false, translateWildcard = true)
806804
else
807805
pt.translateFromRepeated(toArray = false, translateWildcard = true)
@@ -815,7 +813,7 @@ class Typer extends Namer
815813
// We need to make sure its type is no longer nullable
816814
expr0.castToNonNullable
817815
else expr0
818-
val fromCls = if expr1.tpe.stripNull.derivesFrom(defn.ArrayClass)
816+
val fromCls = if expr1.tpe.derivesFrom(defn.ArrayClass)
819817
then defn.ArrayClass else defn.SeqClass
820818
val tpt1 = TypeTree(expr1.tpe.widen.translateToRepeated(fromCls)).withSpan(tree.tpt.span)
821819
assignType(cpy.Typed(tree)(expr1, tpt1), tpt1)
@@ -3866,4 +3864,4 @@ class Typer extends Namer
38663864
EmptyTree
38673865
else typedExpr(call, defn.AnyType)
38683866

3869-
}
3867+
}

tests/explicit-nulls/neg/array.scala

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Test array with nulls.
2+
3+
class ArrayWithNulls {
4+
def test1 = {
5+
// A non-nullable array of non-nullable strings
6+
val a1: Array[String] = Array("hello")
7+
val s1: String = a1(0)
8+
val s2: String | Null = a1(0)
9+
val a2: Array[String] = Array()
10+
11+
// Array type is non-nullable
12+
val b1: Array[String] = null // error
13+
val b2: Array[Int] = null // error
14+
}
15+
16+
def test2 = {
17+
// A nullable array of non-nullable strings
18+
val a1: Array[String] | Null = null
19+
val a2: Array[String] | Null = Array()
20+
val a3: Array[String] | Null = Array("")
21+
val a4: Array[String] | Null = Array("", null) // error
22+
23+
// A non-nullable array of nullable strings
24+
val b1: Array[String | Null] = Array()
25+
val b2: Array[String | Null] = Array(null)
26+
val b3: Array[String | Null] = Array("")
27+
val b4: Array[String | Null] = Array("", null)
28+
val b5: Array[String | Null] = null // error
29+
30+
val s1: String = b1(0) // error
31+
val s2: String | Null = b1(0)
32+
33+
// A nullable array of nullable strings
34+
val c1: Array[String | Null] | Null = Array()
35+
}
36+
37+
def test3 = {
38+
val a1: Array[String] = Array()
39+
40+
val a2: Array[String] | Null = a1
41+
42+
val a3: Array[String | Null] = a1 // error
43+
}
44+
}

tests/explicit-nulls/neg/basic.scala

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,33 @@
11
// Test that reference types are no longer nullable.
22

3-
class Foo {
4-
val s: String = null // error
5-
val s1: String | Null = null
6-
val s2: String | Null = ""
3+
class Basic {
4+
val no: Nothing = ???
75

8-
val b: Boolean = null // error
9-
val c: Int | Null = null
6+
val n: Null = null
7+
val n2: Null = no
8+
9+
val any1: Any = null
10+
val any2: Any = n
11+
12+
val s1: String = null // error
13+
val s2: String = n // error
14+
val s3: String | Null = null
15+
val s4: String | Null = n
16+
val s5: String | Null = ""
1017

1118
val ar1: AnyRef = null // error
12-
val ar2: AnyRef | Null = null
13-
val ob: Object = null // error
19+
val ar2: AnyRef = n // error
20+
val ar3: AnyRef | Null = null
21+
val ob1: Object = null // error
22+
val ob2: Object | Null = null
23+
24+
val b1: Boolean = null // error
25+
val b2: Boolean = n // error
26+
val b3: Boolean | Null = null
27+
28+
val c1: Int = null // error
29+
val c2: Int = n // error
30+
val i3: Int | Null = null
1431

1532
val av: AnyVal = null // error
16-
val a: Any = null
17-
val n: Null = null
1833
}

tests/explicit-nulls/neg/bounds.scala

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,30 @@
1-
val x1: String = ???
2-
val x2: String | Null = ???
1+
// Test Null type in bounds
32

4-
// T has to be nullable type
5-
def f1[T >: Null <: AnyRef | Null](x: T): T = x
3+
class Test {
4+
val x1: String = ???
5+
val x2: String | Null = ???
66

7-
// Null is no longer a subtype of AnyRef, impossible to apply this method directly.
8-
// We can bypass this restriction by importing unsafeNulls.
9-
def f2[T >: Null <: AnyRef](x: T): T = x
7+
// T has to be nullable type
8+
def f1[T >: Null <: AnyRef | Null](x: T): T = x
109

11-
def nullOf[T >: Null <: AnyRef | Null]: T = null
10+
// Null is no longer a subtype of AnyRef, so it is impossible to apply this method directly.
11+
// However, defining this kind of functions is allowed.
12+
// We can bypass this restriction by importing unsafeNulls.
13+
def f2[T >: Null <: AnyRef](x: T): T = x
1214

13-
def g = {
14-
f1(x1)
15-
f1(x2)
15+
def nullOf[T >: Null <: AnyRef | Null]: T = null
1616

17-
f2(x1) // error
18-
f2(x2) // error
17+
def g = {
18+
f1(x1)
19+
f1(x2)
1920

20-
val n1: String = nullOf // error
21-
val n3: String | Null = nullOf
21+
f2(x1) // error
22+
f2(x2) // error
23+
24+
val n1: String = nullOf // error
25+
val n3: String | Null = nullOf
26+
}
27+
28+
// Bounds in class definition is strictly checked
29+
class A[T >: Null <: AnyRef] {} // error: conflicting bounds Null <: ... <: AnyRef
2230
}

tests/explicit-nulls/neg/default.scala

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
class Foo {
2-
val x: String = null // error: String is non-nullable
2+
val s: String = null // error: String is non-nullable
33

44
def foo(x: String): String = "x"
55

6-
val y = foo(null) // error: String argument is non-nullable
6+
val fn = foo(null) // error: String argument is non-nullable
77

8-
val z: String = foo("hello")
8+
val x: String = foo("hello")
9+
10+
val arr: Array[Int] = null // error: Array is non-nullable
11+
12+
val t: (String, Int) = null // error: Tuple is non-nullable
13+
14+
val f: String => Int = null // error: Function is non-nullable
915

1016
class Bar
1117
val b: Bar = null // error: user-created classes are also non-nullable
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-- [E008] Not Found Error: tests/explicit-nulls/neg/nn-basic.scala:3:21 ------------------------------------------------
2+
3 | val s2: String = s.trim // error
3+
| ^^^^^^
4+
| value trim is not a member of String | Null.
5+
| Since explicit-nulls is enabled, the selection is rejected because
6+
| String | Null could be null at runtime.
7+
| If you want to select trim without checking for a null value,
8+
| insert a .nn before .trim or import scala.language.unsafeNulls.
9+
-- [E008] Not Found Error: tests/explicit-nulls/neg/nn-basic.scala:5:17 ------------------------------------------------
10+
5 | val l: Int = s.length // error
11+
| ^^^^^^^^
12+
| value length is not a member of String | Null.
13+
| Since explicit-nulls is enabled, the selection is rejected because
14+
| String | Null could be null at runtime.
15+
| If you want to select length without checking for a null value,
16+
| insert a .nn before .length or import scala.language.unsafeNulls.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class Test {
2+
val s: String | Null = ???
3+
val s2: String = s.trim // error
4+
val s3: String | Null = s.nn.trim
5+
val l: Int = s.length // error
6+
}

tests/explicit-nulls/neg/nn.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,17 @@
22

33
class Test {
44
val s1: String | Null = ???
5-
val s2: String = s1.nn
5+
val s2: String = s1 // error
6+
val s3: String = s1.nn
7+
8+
val l1: Int = s1.length // error
9+
val l2: Int = s1.nn.length
610

711
val ss1: Array[String | Null] | Null = ???
812
val ss2: Array[String | Null] = ss1.nn
913
val ss3: Array[String] = ss1.nn // error
14+
val ss4: Array[String] = ss1.asInstanceOf
15+
16+
val a1: String | Null = ss1(0) // error
17+
val a2: String | Null = ss1.nn(0)
1018
}

tests/explicit-nulls/neg/nullable-types.scala

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)