Skip to content

Commit a04a482

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

File tree

7 files changed

+32
-16
lines changed

7 files changed

+32
-16
lines changed

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

Lines changed: 11 additions & 10 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,7 +97,7 @@ 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? */
@@ -147,6 +147,7 @@ object JavaNullInterop {
147147
outermostLevelAlreadyNullable = oldOutermostNullable
148148
derivedLambdaType(mtp)(paramInfos2, this(mtp.resType))
149149
case tp: TypeAlias => mapOver(tp)
150+
case tp: TypeBounds => mapOver(tp)
150151
case tp: AndType =>
151152
// nullify(A & B) = (nullify(A) & nullify(B)) | Null, but take care not to add
152153
// duplicate `Null`s at the outermost level inside `A` and `B`.

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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ 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
89
}
910

1011
class Inherit_2 extends Unsafe_1 {
1112
override def foo(s: String | Null): String | Null = null
1213
override def bar[T >: String](s: T | Null): T | Null = s
1314
override def bar2[T >: String](s: T): T = s
15+
override def bar3[T <: Function1[(String|Null),(String|Null)]](g: T) = g
1416
}
1517

1618
class Inherit_3 extends Unsafe_1 {
@@ -23,6 +25,8 @@ class Inherit_4 extends Unsafe_1 {
2325
override def bar[T >: String](s: T | Null): T = "non-null string"
2426
}
2527

28+
case class cc()
29+
2630
@main
2731
def Flexible_2() =
2832
val s2: String | Null = "foo"

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ 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
1515
}
1616

1717
object Foo {
1818
def bar = "bar!"
19+
def id[T](t: T): T = t
1920
}

0 commit comments

Comments
 (0)