Skip to content

Commit 77c3c0c

Browse files
committed
Only nullify tasty files if flexible types are enabled
1 parent 3cdda29 commit 77c3c0c

File tree

8 files changed

+38
-20
lines changed

8 files changed

+38
-20
lines changed

compiler/src/dotty/tools/dotc/core/JavaNullInterop.scala renamed to compiler/src/dotty/tools/dotc/core/ImplicitNullInterop.scala

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import dotty.tools.dotc.core.Decorators.i
3535
* to handle the full spectrum of Scala types. Additionally, some kinds of symbols like constructors and
3636
* enum instances get special treatment.
3737
*/
38-
object JavaNullInterop {
38+
object ImplicitNullInterop {
3939

4040
/** Transforms the type `tp` of Java member `sym` to be explicitly nullable.
4141
* `tp` is needed because the type inside `sym` might not be set when this method is called.
@@ -55,11 +55,11 @@ object JavaNullInterop {
5555
*/
5656
def nullifyMember(sym: Symbol, tp: Type, isEnumValueDef: Boolean)(using Context): Type = trace(i"nullifyMember ${sym}, ${tp}"){
5757
assert(ctx.explicitNulls)
58-
assert(sym.is(JavaDefined), "can only nullify java-defined members")
5958

6059
// Some special cases when nullifying the type
61-
if isEnumValueDef || sym.name == nme.TYPE_ then
62-
// Don't nullify the `TYPE` field in every class and Java enum instances
60+
if isEnumValueDef || sym.name == nme.TYPE_ // Don't nullify the `TYPE` field in every class and Java enum instances
61+
|| sym.is(Flags.ModuleVal) // Don't nullify Modules
62+
then
6363
tp
6464
else if sym.name == nme.toString_ || sym.isConstructor || hasNotNullAnnot(sym) then
6565
// Don't nullify the return type of the `toString` method.
@@ -80,14 +80,14 @@ object JavaNullInterop {
8080
* but the result type is not nullable.
8181
*/
8282
private def nullifyExceptReturnType(tp: Type)(using Context): Type =
83-
new JavaNullMap(outermostLevelAlreadyNullable = true)(tp)
83+
new ImplicitNullMap(outermostLevelAlreadyNullable = true)(tp)
8484

85-
/** Nullifies a Java type by adding `| Null` in the relevant places. */
85+
/** Nullifies a type by adding `| Null` in the relevant places. */
8686
private def nullifyType(tp: Type)(using Context): Type =
87-
new JavaNullMap(outermostLevelAlreadyNullable = false)(tp)
87+
new ImplicitNullMap(outermostLevelAlreadyNullable = false)(tp)
8888

89-
/** A type map that implements the nullification function on types. Given a Java-sourced type, this adds `| Null`
90-
* in the right places to make the nulls explicit in Scala.
89+
/** A type map that implements the nullification function on types. Given a Java-sourced type or an
90+
* implicitly null type, this adds `| Null` in the right places to make the nulls explicit.
9191
*
9292
* @param outermostLevelAlreadyNullable whether this type is already nullable at the outermost level.
9393
* For example, `Array[String] | Null` is already nullable at the
@@ -97,17 +97,16 @@ object JavaNullInterop {
9797
* This is useful for e.g. constructors, and also so that `A & B` is nullified
9898
* to `(A & B) | Null`, instead of `(A | Null & B | Null) | Null`.
9999
*/
100-
private class JavaNullMap(var outermostLevelAlreadyNullable: Boolean)(using Context) extends TypeMap {
100+
private class ImplicitNullMap(var outermostLevelAlreadyNullable: Boolean)(using Context) extends TypeMap {
101101
def nullify(tp: Type): Type = if ctx.flexibleTypes then FlexibleType(tp) else OrNull(tp)
102102

103103
/** Should we nullify `tp` at the outermost level? */
104104
def needsNull(tp: Type): Boolean =
105105
if outermostLevelAlreadyNullable then false
106106
else tp match
107-
case tp: TypeRef if !tp.hasSimpleKind => false
108-
case tp: TypeRef if
107+
case tp: TypeRef if !tp.hasSimpleKind
109108
// We don't modify value types because they're non-nullable even in Java.
110-
tp.symbol.isValueClass
109+
|| tp.symbol.isValueClass
111110
// We don't modify unit types.
112111
|| tp.isRef(defn.UnitClass)
113112
// We don't modify `Any` because it's already nullable.
@@ -147,6 +146,7 @@ object JavaNullInterop {
147146
outermostLevelAlreadyNullable = oldOutermostNullable
148147
derivedLambdaType(mtp)(paramInfos2, this(mtp.resType))
149148
case tp: TypeAlias => mapOver(tp)
149+
case tp: TypeBounds => mapOver(tp)
150150
case tp: AndType =>
151151
// nullify(A & B) = (nullify(A) & nullify(B)) | Null, but take care not to add
152152
// duplicate `Null`s at the outermost level inside `A` and `B`.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3407,7 +3407,7 @@ object Types extends TypeUtils {
34073407

34083408
object FlexibleType {
34093409
def apply(tp: Type)(using Context): FlexibleType =
3410-
// assert(tp.isValueType, s"Should not flexify ${tp}")
3410+
assert(tp.isValueType, s"Should not flexify ${tp}")
34113411
tp match {
34123412
case ft: FlexibleType => ft
34133413
case _ =>

compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ class ClassfileParser(
519519
denot.info = translateTempPoly(attrCompleter.complete(denot.info, isVarargs))
520520
if (isConstructor) normalizeConstructorInfo()
521521

522-
if (ctx.explicitNulls) denot.info = JavaNullInterop.nullifyMember(denot.symbol, denot.info, isEnum)
522+
if (ctx.explicitNulls) denot.info = ImplicitNullInterop.nullifyMember(denot.symbol, denot.info, isEnum)
523523

524524
// seal java enums
525525
if (isEnum) {

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,7 @@ class TreeUnpickler(reader: TastyReader,
921921
// If explicit nulls is enabled, and the source file did not have explicit
922922
// nulls enabled, nullify the member to allow for compatibility.
923923
def nullify(sym: Symbol) =
924-
if (ctx.explicitNulls && !explicitNulls) then
924+
if (ctx.explicitNulls && ctx.flexibleTypes && !explicitNulls) then
925925
sym.info = ImplicitNullInterop.nullifyMember(sym, sym.info, sym.is(Enum))
926926

927927
val name = readName()

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1907,7 +1907,7 @@ class Namer { typer: Typer =>
19071907

19081908
val mbrTpe = paramFn(checkSimpleKinded(typedAheadType(mdef.tpt, tptProto)).tpe)
19091909
if (ctx.explicitNulls && mdef.mods.is(JavaDefined))
1910-
JavaNullInterop.nullifyMember(sym, mbrTpe, mdef.mods.isAllOf(JavaEnumValue))
1910+
ImplicitNullInterop.nullifyMember(sym, mbrTpe, mdef.mods.isAllOf(JavaEnumValue))
19111911
else mbrTpe
19121912
}
19131913

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,18 @@ class CompilationTests {
217217
compileFilesInDir("tests/explicit-nulls/pos", explicitNullsOptions),
218218
compileFilesInDir("tests/explicit-nulls/flexible-types-common", explicitNullsOptions),
219219
compileFilesInDir("tests/explicit-nulls/unsafe-common", explicitNullsOptions and "-language:unsafeNulls" and "-Yno-flexible-types"),
220-
)
221-
}.checkCompile()
220+
).checkCompile()
221+
222+
locally {
223+
val tests = List(
224+
compileFile("tests/explicit-nulls/flexible-unpickle/Unsafe_1.scala", explicitNullsOptions without "-Yexplicit-nulls"),
225+
compileFile("tests/explicit-nulls/flexible-unpickle/Flexible_2.scala", explicitNullsOptions.withClasspath(
226+
defaultOutputDir + testGroup + "/Unsafe_1/flexible-unpickle/Unsafe_1")),
227+
).map(_.keepOutput.checkCompile())
228+
229+
tests.foreach(_.delete())
230+
}
231+
}
222232

223233
@Test def explicitNullsWarn: Unit = {
224234
implicit val testGroup: TestGroup = TestGroup("explicitNullsWarn")

tests/explicit-nulls/flexible-unpickle/Flexible_2.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@ class Inherit_1 extends Unsafe_1 {
55
override def foo(s: String): String = s
66
override def bar[T >: String](s: T): T = s
77
override def bar2[T >: String | Null](s: T): T = s
8+
override def bar3[T <: Function1[String,String]](g: T) = g
9+
override def bar4[HK[_]](i: String | Null): HK[String | Null] = ???
810
}
911

1012
class Inherit_2 extends Unsafe_1 {
1113
override def foo(s: String | Null): String | Null = null
1214
override def bar[T >: String](s: T | Null): T | Null = s
1315
override def bar2[T >: String](s: T): T = s
16+
override def bar3[T <: Function1[(String|Null),(String|Null)]](g: T) = g
17+
override def bar4[HK[_]](i: String): HK[String] = ???
1418
}
1519

1620
class Inherit_3 extends Unsafe_1 {
@@ -23,6 +27,8 @@ class Inherit_4 extends Unsafe_1 {
2327
override def bar[T >: String](s: T | Null): T = "non-null string"
2428
}
2529

30+
case class cc()
31+
2632
@main
2733
def Flexible_2() =
2834
val s2: String | Null = "foo"

tests/explicit-nulls/flexible-unpickle/Unsafe_1.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ class Unsafe_1 {
1111
def bar2[T >: String | Null](s: T): T = {
1212
???
1313
}
14-
def bar3[T <: Int => Int](g: T): T = g
14+
def bar3[T <: Function1[String,String]](g: T): T = g
15+
def bar4[HK[_]](i: String): HK[String] = ???
1516
}
1617

1718
object Foo {
1819
def bar = "bar!"
20+
def id[T](t: T): T = t
1921
}

0 commit comments

Comments
 (0)