diff --git a/compiler/src/dotty/tools/dotc/core/ImplicitNullInterop.scala b/compiler/src/dotty/tools/dotc/core/JavaNullInterop.scala similarity index 91% rename from compiler/src/dotty/tools/dotc/core/ImplicitNullInterop.scala rename to compiler/src/dotty/tools/dotc/core/JavaNullInterop.scala index 783d373f8902..f3e62bc36e06 100644 --- a/compiler/src/dotty/tools/dotc/core/ImplicitNullInterop.scala +++ b/compiler/src/dotty/tools/dotc/core/JavaNullInterop.scala @@ -35,7 +35,7 @@ import dotty.tools.dotc.core.Decorators.i * to handle the full spectrum of Scala types. Additionally, some kinds of symbols like constructors and * enum instances get special treatment. */ -object ImplicitNullInterop { +object JavaNullInterop { /** Transforms the type `tp` of Java member `sym` to be explicitly nullable. * `tp` is needed because the type inside `sym` might not be set when this method is called. @@ -55,11 +55,11 @@ object ImplicitNullInterop { */ def nullifyMember(sym: Symbol, tp: Type, isEnumValueDef: Boolean)(using Context): Type = trace(i"nullifyMember ${sym}, ${tp}"){ assert(ctx.explicitNulls) + assert(sym.is(JavaDefined), "can only nullify java-defined members") // Some special cases when nullifying the type - if isEnumValueDef || sym.name == nme.TYPE_ // Don't nullify the `TYPE` field in every class and Java enum instances - || sym.is(Flags.ModuleVal) // Don't nullify Modules - then + if isEnumValueDef || sym.name == nme.TYPE_ then + // Don't nullify the `TYPE` field in every class and Java enum instances tp else if sym.name == nme.toString_ || sym.isConstructor || hasNotNullAnnot(sym) then // Don't nullify the return type of the `toString` method. @@ -80,14 +80,14 @@ object ImplicitNullInterop { * but the result type is not nullable. */ private def nullifyExceptReturnType(tp: Type)(using Context): Type = - new ImplicitNullMap(outermostLevelAlreadyNullable = true)(tp) + new JavaNullMap(outermostLevelAlreadyNullable = true)(tp) - /** Nullifies a type by adding `| Null` in the relevant places. */ + /** Nullifies a Java type by adding `| Null` in the relevant places. */ private def nullifyType(tp: Type)(using Context): Type = - new ImplicitNullMap(outermostLevelAlreadyNullable = false)(tp) + new JavaNullMap(outermostLevelAlreadyNullable = false)(tp) - /** A type map that implements the nullification function on types. Given a Java-sourced type or an - * implicitly null type, this adds `| Null` in the right places to make the nulls explicit. + /** A type map that implements the nullification function on types. Given a Java-sourced type, this adds `| Null` + * in the right places to make the nulls explicit in Scala. * * @param outermostLevelAlreadyNullable whether this type is already nullable at the outermost level. * For example, `Array[String] | Null` is already nullable at the @@ -97,7 +97,7 @@ object ImplicitNullInterop { * This is useful for e.g. constructors, and also so that `A & B` is nullified * to `(A & B) | Null`, instead of `(A | Null & B | Null) | Null`. */ - private class ImplicitNullMap(var outermostLevelAlreadyNullable: Boolean)(using Context) extends TypeMap { + private class JavaNullMap(var outermostLevelAlreadyNullable: Boolean)(using Context) extends TypeMap { def nullify(tp: Type): Type = if ctx.flexibleTypes then FlexibleType(tp) else OrNull(tp) /** Should we nullify `tp` at the outermost level? */ diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 0081796b1e1c..c7456006f0ed 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3432,7 +3432,6 @@ object Types extends TypeUtils { // flexible type is always a subtype of the original type and the Object type. // It is not necessary according to the use cases, so we choose to use a simpler // rule. - assert(!tp.isInstanceOf[LazyType]) FlexibleType(OrNull(tp), tp) } } diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 4daf49aa8d2b..cfbdc854a88f 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -525,7 +525,7 @@ class ClassfileParser( denot.info = translateTempPoly(attrCompleter.complete(denot.info, isVarargs)) if (isConstructor) normalizeConstructorInfo() - if (ctx.explicitNulls) denot.info = ImplicitNullInterop.nullifyMember(denot.symbol, denot.info, isEnum) + if (ctx.explicitNulls) denot.info = JavaNullInterop.nullifyMember(denot.symbol, denot.info, isEnum) // seal java enums if (isEnum) { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 67bbbe4a66ca..d6f2812dad0d 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -981,12 +981,6 @@ class TreeUnpickler(reader: TastyReader, sym.info = tpt.tpe ValDef(tpt) } - - // If explicit nulls is enabled, and the source file did not have explicit - // nulls enabled, nullify the member to allow for compatibility. - if (ctx.explicitNulls && !explicitNulls) then - sym.info = ImplicitNullInterop.nullifyMember(sym, sym.info, sym.is(Enum)) - goto(end) setSpan(start, tree) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 91823f64cc19..89dc4cf53472 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1902,7 +1902,7 @@ class Namer { typer: Typer => val mbrTpe = paramFn(checkSimpleKinded(typedAheadType(mdef.tpt, tptProto)).tpe) if (ctx.explicitNulls && mdef.mods.is(JavaDefined)) - ImplicitNullInterop.nullifyMember(sym, mbrTpe, mdef.mods.isAllOf(JavaEnumValue)) + JavaNullInterop.nullifyMember(sym, mbrTpe, mdef.mods.isAllOf(JavaEnumValue)) else mbrTpe } diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 05d92c93cf76..7ba50bcda544 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -217,18 +217,8 @@ class CompilationTests { compileFilesInDir("tests/explicit-nulls/pos", explicitNullsOptions), compileFilesInDir("tests/explicit-nulls/flexible-types-common", explicitNullsOptions), compileFilesInDir("tests/explicit-nulls/unsafe-common", explicitNullsOptions and "-language:unsafeNulls" and "-Yno-flexible-types"), - ).checkCompile() - - locally { - val tests = List( - compileFile("tests/explicit-nulls/flexible-unpickle/Unsafe_1.scala", explicitNullsOptions without "-Yexplicit-nulls"), - compileFile("tests/explicit-nulls/flexible-unpickle/Flexible_2.scala", explicitNullsOptions.withClasspath( - defaultOutputDir + testGroup + "/Unsafe_1/flexible-unpickle/Unsafe_1")), - ).map(_.keepOutput.checkCompile()) - - tests.foreach(_.delete()) - } - } + ) + }.checkCompile() @Test def explicitNullsWarn: Unit = { implicit val testGroup: TestGroup = TestGroup("explicitNullsWarn") diff --git a/tests/explicit-nulls/flexible-unpickle/Flexible_2.scala b/tests/explicit-nulls/flexible-unpickle/Flexible_2.scala deleted file mode 100644 index 413e1a5f237c..000000000000 --- a/tests/explicit-nulls/flexible-unpickle/Flexible_2.scala +++ /dev/null @@ -1,10 +0,0 @@ -import unsafeNulls.Foo.* -import unsafeNulls.Unsafe_1 - -@main -def Flexible_2() = - val s2: String | Null = "foo" - val unsafe = new Unsafe_1() - val s: String = unsafe.foo(s2) - unsafe.foo("") - unsafe.foo(null) \ No newline at end of file diff --git a/tests/explicit-nulls/flexible-unpickle/Unsafe_1.scala b/tests/explicit-nulls/flexible-unpickle/Unsafe_1.scala deleted file mode 100644 index 77c3087fef70..000000000000 --- a/tests/explicit-nulls/flexible-unpickle/Unsafe_1.scala +++ /dev/null @@ -1,12 +0,0 @@ -package unsafeNulls - -class Unsafe_1 { - def foo(s: String): String = { - if (s == null) then "nullString" - else s - } -} - -object Foo { - def bar = "bar!" -} \ No newline at end of file