Skip to content

Commit 24d6ba9

Browse files
committed
Update code
1 parent a5177e1 commit 24d6ba9

28 files changed

+271
-30
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ object Contexts {
464464
def explicitNulls: Boolean = base.settings.YexplicitNulls.value
465465

466466
/** Is the flexible types option set? */
467-
def flexibleTypes: Boolean = base.settings.YflexibleTypes.value
467+
def flexibleTypes: Boolean = base.settings.YexplicitNulls.value && base.settings.YflexibleTypes.value
468468

469469
/** A fresh clone of this context embedded in this context. */
470470
def fresh: FreshContext = freshOver(this)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ object JavaNullInterop {
122122
// We don't make the outmost levels of type arguments nullable if tycon is Java-defined.
123123
// This is because Java classes are _all_ nullified, so both `java.util.List[String]` and
124124
// `java.util.List[String|Null]` contain nullable elements.
125-
outermostLevelAlreadyNullable = tp.classSymbol.is(JavaDefined) && !ctx.flexibleTypes
125+
outermostLevelAlreadyNullable = tp.classSymbol.is(JavaDefined)
126126
val targs2 = targs map this
127127
outermostLevelAlreadyNullable = oldOutermostNullable
128128
val appTp2 = derivedAppliedType(appTp, tycon, targs2)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ object NullOpsDecorator:
3939
if (tp1s ne tp1) && (tp2s ne tp2) then
4040
tp.derivedAndType(tp1s, tp2s)
4141
else tp
42-
case tp @ FlexibleType(tp1) => strip(tp1)
42+
case tp: FlexibleType => tp.hi
4343
case tp @ TypeBounds(lo, hi) =>
4444
tp.derivedTypeBounds(strip(lo), strip(hi))
4545
case tp => tp

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
545545
// invariant: tp2 is NOT a FlexibleType
546546
// is Flex(T) <: tp2?
547547
case tp1: FlexibleType =>
548-
recur(tp1.underlying, tp2)
548+
recur(tp1.hi, tp2)
549549
case CapturingType(parent1, refs1) =>
550550
if tp2.isAny then true
551551
else if subCaptures(refs1, tp2.captureSet, frozenConstraint).isOK && sameBoxed(tp1, tp2, refs1)

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

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3408,19 +3408,28 @@ object Types {
34083408
* in Kotlin. A FlexibleType(T) generally behaves like an abstract type with bad bounds
34093409
* T|Null .. T, so that T|Null <: FlexibleType(T) <: T.
34103410
*/
3411+
case class FlexibleType(underlying: Type, lo: Type, hi: Type) extends CachedGroundType with ValueType {
3412+
def derivedFlexibleType(underlying: Type)(using Context): Type =
3413+
if this.underlying eq underlying then this else FlexibleType(underlying)
3414+
3415+
override def computeHash(bs: Binders): Int = doHash(bs, underlying)
3416+
3417+
override final def baseClasses(using Context): List[ClassSymbol] = underlying.baseClasses
3418+
}
34113419

34123420
object FlexibleType {
3413-
def apply(underlying: Type) = underlying match {
3421+
def apply(underlying: Type)(using Context): FlexibleType = underlying match {
34143422
case ft: FlexibleType => ft
3415-
case _ => new FlexibleType(underlying)
3423+
case _ =>
3424+
val hi = underlying.stripNull
3425+
val lo = if hi eq underlying then OrNull(hi) else underlying
3426+
new FlexibleType(underlying, lo, hi)
3427+
}
3428+
3429+
def unapply(tp: Type)(using Context): Option[Type] = tp match {
3430+
case ft: FlexibleType => Some(ft.underlying)
3431+
case _ => None
34163432
}
3417-
}
3418-
case class FlexibleType(underlying: Type) extends CachedGroundType with ValueType {
3419-
def lo(using Context): Type = OrNull(underlying)
3420-
def derivedFlexibleType(under: Type)(using Context): Type =
3421-
if this.underlying eq under then this else FlexibleType(under)
3422-
override def computeHash(bs: Binders): Int = doHash(bs, underlying)
3423-
override final def baseClasses(using Context): List[ClassSymbol] = underlying.baseClasses
34243433
}
34253434

34263435
// --- AndType/OrType ---------------------------------------------------------------
@@ -5662,8 +5671,8 @@ object Types {
56625671
val args1 = args.zipWithConserve(tparams):
56635672
case (arg @ TypeBounds(lo, hi), tparam) =>
56645673
boundFollowingVariance(lo, hi, tparam)
5665-
case (arg @ FlexibleType(lo, hi), tparam) =>
5666-
boundFollowingVariance(arg.lo, hi, tparam)
5674+
case (arg: FlexibleType, tparam) =>
5675+
boundFollowingVariance(arg.lo, arg.hi, tparam)
56675676
case (arg, _) => arg
56685677
tp.derivedAppliedType(tycon, args1)
56695678
case tp: RefinedType =>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ object Nullables:
3535

3636
private def nullifiedHi(lo: Type, hi: Type)(using Context): Type =
3737
if needNullifyHi(lo, hi) then
38-
if ctx.flexibleTypes then FlexibleType(hi) else OrType(hi, defn.NullType, soft = false)
38+
if ctx.flexibleTypes then FlexibleType(hi) else OrNull(hi)
3939
else hi
4040

4141
/** Create a nullable type bound

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -205,16 +205,24 @@ class CompilationTests {
205205
}.checkRuns()
206206

207207
// Flexible types tests
208-
@Test def flexibleTypesRun: Unit = {
209-
implicit val testGroup: TestGroup = TestGroup("flexibleTypesRun")
210-
compileFilesInDir("tests/flexible-types/run", flexibleTypesOptions)
211-
}.checkRuns()
208+
// @Test def flexibleTypesRun: Unit = {
209+
// implicit val testGroup: TestGroup = TestGroup("flexibleTypesRun")
210+
// compileFilesInDir("tests/flexible-types/run", flexibleTypesOptions)
211+
// }.checkRuns()
212+
213+
@Test def flexibleTypesNeg: Unit = {
214+
implicit val testGroup: TestGroup = TestGroup("flexibleTypesNeg")
215+
aggregateTests(
216+
compileFilesInDir("tests/explicit-nulls/flexible-types/neg", defaultOptions and "-Yexplicit-nulls"),
217+
compileFilesInDir("tests/explicit-nulls/flexible-types/common", defaultOptions and "-Yexplicit-nulls"),
218+
)
219+
}.checkExpectedErrors()
212220

213221
@Test def flexibleTypesPos: Unit = {
214222
implicit val testGroup: TestGroup = TestGroup("flexibleTypesPos")
215223
aggregateTests(
216-
compileFilesInDir("tests/flexible-types/pos", flexibleTypesOptions),
217-
compileFilesInDir("tests/flexible-types/pos-separate", flexibleTypesOptions)
224+
compileFilesInDir("tests/explicit-nulls/flexible-types/pos", flexibleTypesOptions),
225+
compileFilesInDir("tests/explicit-nulls/flexible-types/common", flexibleTypesOptions),
218226
)
219227
}.checkCompile()
220228

compiler/test/dotty/tools/vulpix/TestConfiguration.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ object TestConfiguration {
8585
val picklingWithCompilerOptions =
8686
picklingOptions.withClasspath(withCompilerClasspath).withRunClasspath(withCompilerClasspath)
8787

88+
val explicitNullsOptions = defaultOptions and "-Yexplicit-nulls"
89+
8890
val flexibleTypesOptions = explicitNullsOptions and "-Yflexible-types"
8991

9092
/** Default target of the generated class files */
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
public class J {
2+
public String f() {
3+
return "";
4+
}
5+
6+
public <T> T g() {
7+
return null;
8+
}
9+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Check Java calls have been cast to non-nullable.
2+
3+
def test[T <: AnyRef] =
4+
val j: J = new J
5+
6+
val s = j.f()
7+
val sn = s == null
8+
val sn2 = s != null
9+
val seqn = s eq null
10+
val seqn2 = null eq s
11+
12+
13+
val t = j.g[T]()
14+
val tn = t == null
15+
val tn2 = t != null
16+
val teqn = t eq null
17+
val teqn2 = null eq t

tests/flexible-types/pos/i7883.scala renamed to tests/explicit-nulls/flexible-types/common/i7883.scala

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,4 @@ object Test extends App {
66
case r(hd, tl) => Some((hd, tl)) // error // error // error
77
case _ => None
88
}
9-
10-
def headUnsafe(s: String, r: Regex): Option[(String, String)] =
11-
import scala.language.unsafeNulls
12-
s.trim match {
13-
case r(hd, tl) => Some((hd, tl))
14-
case _ => None
15-
}
169
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
public class J {
2+
3+
public class K {}
4+
5+
public J self() {
6+
return this;
7+
}
8+
9+
public String f1() {
10+
return "";
11+
}
12+
13+
public int f2() {
14+
return 0;
15+
}
16+
17+
public K f3() {
18+
return null;
19+
}
20+
21+
public <T> T g1() {
22+
return null;
23+
}
24+
}
25+
26+
class J2<T> {
27+
public T x = null;
28+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Check Java calls have been cast to non-nullable.
2+
3+
val j: J = new J
4+
5+
val jj = j.self()
6+
7+
val s1: String = j.f1() // error
8+
9+
val s1n: String | Null = j.f1()
10+
11+
val i1: Int = j.f2()
12+
13+
val k: jj.K = jj.f3()
14+
15+
val s2: String = j.g1[String]() // error
16+
17+
val s2n: String | Null = j.g1[String]()
18+
19+
val s3: String = j.g1[String | Null]() // error
20+
21+
val s3n: String | Null = j.g1[String | Null]()
22+
23+
val i2: Int = j.g1[Int]() // error
24+
25+
val a1: Any = j.g1[Any]()
26+
27+
val ar1: AnyRef = j.g1[AnyRef]() // error
28+
29+
val n1: Null = j.g1[Null]()
30+
31+
def clo1[T]: T = j.g1[T]() // error
32+
33+
def clo3[T >: Null <: AnyRef | Null]: T = j.g1[T]()
34+
35+
def testJ2[T]: T =
36+
val j2: J2[T] = new J2
37+
j2.x // error
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class J1 {
2+
J2 getJ2() { return new J2(); }
3+
}
4+
5+
class J2 {
6+
J1 getJ1() { return new J1(); }
7+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class S {
2+
val j: J2 = new J2()
3+
j.getJ1().getJ2().getJ1().getJ2().getJ1().getJ2() // error
4+
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
abstract class J {
22
abstract void foo(String... x);
3-
abstract void bar(String x, String... y);
43
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
class S {
2+
val j: J = ???
3+
4+
j.foo()
5+
j.foo("")
6+
j.foo(null)
7+
j.foo("", "")
8+
j.foo("", null, "")
9+
10+
val arg1: Array[String] = ???
11+
val arg2: Array[String | Null] = ???
12+
val arg3: Array[String] | Null = ???
13+
val arg4: Array[String | Null] | Null = ???
14+
15+
j.foo(arg1: _*)
16+
j.foo(arg2: _*)
17+
// TODO: fix for flexible types
18+
j.foo(arg3: _*) // error
19+
j.foo(arg4: _*) // error
20+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Test that we can select through "| Null" is unsafeNulls is enabled (unsoundly).
2+
3+
class Foo {
4+
import java.util.ArrayList
5+
import java.util.Iterator
6+
7+
val x3 = new ArrayList[ArrayList[ArrayList[String]]]()
8+
val x4: Int = x3.get(0).get(0).get(0).length() // error
9+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class S {
2+
locally {
3+
// OfType Implicits
4+
5+
import java.nio.charset.StandardCharsets
6+
import scala.io.Codec
7+
8+
val c: Codec = StandardCharsets.UTF_8 // error
9+
}
10+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import java.nio.file.Paths
2+
3+
def test1 = {
4+
Paths.get("")
5+
Paths.get("", null)
6+
Paths.get("", "")
7+
Paths.get("", "", null)
8+
9+
val x1: String = ???
10+
val x2: String | Null = ???
11+
12+
Paths.get("", x1)
13+
Paths.get("", x2)
14+
}
15+
16+
def test2 = {
17+
val xs1: Seq[String] = ???
18+
val xs2: Seq[String | Null] = ???
19+
val xs3: Seq[String | Null] | Null = ???
20+
val xs4: Seq[String] | Null = ???
21+
22+
val ys1: Array[String] = ???
23+
val ys2: Array[String | Null] = ???
24+
val ys3: Array[String | Null] | Null = ???
25+
val ys4: Array[String] | Null = ???
26+
27+
Paths.get("", xs1: _*)
28+
Paths.get("", xs2: _*)
29+
Paths.get("", xs3: _*) // error
30+
Paths.get("", xs4: _*) // error
31+
32+
Paths.get("", ys1: _*)
33+
Paths.get("", ys2: _*)
34+
Paths.get("", ys3: _*) // error
35+
Paths.get("", ys4: _*) // error
36+
37+
Paths.get("", null: _*) // error
38+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
def test1 =
2+
val s: String = ???
3+
s.trim() match
4+
case _: String =>
5+
case null => // error: Values of types Null and String cannot be compared
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class S {
2+
class O {
3+
type I = Int
4+
val a: I = 1
5+
6+
type S = String | Null
7+
val s: S = ""
8+
}
9+
10+
def f = {
11+
val o: O = new O
12+
val m: O | Null = o
13+
val n0: o.I = o.a
14+
val n1: m.I = 0 // error
15+
val n2: Int = m.a // error
16+
17+
val s1: m.S = ??? // error
18+
val s2: m.S | Null = ??? // error
19+
val s3: String = m.s // error
20+
val ss: String = o.s // error
21+
}
22+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import java.util.ArrayList
2+
3+
def f[T]: ArrayList[T] = {
4+
val cz = Class.forName("java.util.ArrayList")
5+
val o = cz.newInstance() // error: T of Class[?] | Null
6+
o.asInstanceOf[ArrayList[T]]
7+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public class J<T> {
2+
public J<T> j = this;
3+
}

0 commit comments

Comments
 (0)