Skip to content

Commit 66006a4

Browse files
committed
Add unsafeJavaReturn
1 parent aff1e11 commit 66006a4

File tree

10 files changed

+58
-12
lines changed

10 files changed

+58
-12
lines changed

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,8 +355,11 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
355355
.setTyper(new Typer)
356356
.addMode(Mode.ImplicitsEnabled)
357357
.setTyperState(ctx.typerState.fresh(ctx.reporter))
358-
if ctx.settings.YexplicitNulls.value && !Feature.enabledBySetting(nme.unsafeNulls) then
359-
start = start.addMode(Mode.SafeNulls)
358+
if ctx.settings.YexplicitNulls.value then
359+
if !Feature.enabledBySetting(nme.unsafeNulls) then
360+
start = start.addMode(Mode.SafeNulls)
361+
if Feature.enabledBySetting(nme.unsafeJavaReturn) then
362+
start = start.addMode(Mode.UnsafeJavaReturn)
360363
ctx.initialize()(using start) // re-initialize the base context with start
361364

362365
// `this` must be unchecked for safe initialization because by being passed to setRun during

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

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -659,12 +659,19 @@ object Contexts {
659659
def setProfiler(profiler: Profiler): this.type = updateStore(profilerLoc, profiler)
660660
def setNotNullInfos(notNullInfos: List[NotNullInfo]): this.type = updateStore(notNullInfosLoc, notNullInfos)
661661
def setImportInfo(importInfo: ImportInfo): this.type =
662-
importInfo.mentionsFeature(nme.unsafeNulls) match
663-
case Some(true) =>
664-
setMode(this.mode &~ Mode.SafeNulls)
665-
case Some(false) if ctx.settings.YexplicitNulls.value =>
666-
setMode(this.mode | Mode.SafeNulls)
667-
case _ =>
662+
if ctx.settings.YexplicitNulls.value then
663+
importInfo.mentionsFeature(nme.unsafeNulls) match
664+
case Some(true) =>
665+
setMode(this.mode &~ Mode.SafeNulls)
666+
case Some(false) =>
667+
setMode(this.mode | Mode.SafeNulls)
668+
case _ =>
669+
importInfo.mentionsFeature(nme.unsafeJavaReturn) match
670+
case Some(true) =>
671+
setMode(this.mode | Mode.UnsafeJavaReturn)
672+
case Some(false) =>
673+
setMode(this.mode &~ Mode.UnsafeJavaReturn)
674+
case _ =>
668675
updateStore(importInfoLoc, importInfo)
669676
def setTypeAssigner(typeAssigner: TypeAssigner): this.type = updateStore(typeAssignerLoc, typeAssigner)
670677

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,7 @@ class Definitions {
989989
@tu lazy val TargetNameAnnot: ClassSymbol = requiredClass("scala.annotation.targetName")
990990
@tu lazy val VarargsAnnot: ClassSymbol = requiredClass("scala.annotation.varargs")
991991
@tu lazy val SinceAnnot: ClassSymbol = requiredClass("scala.annotation.since")
992+
@tu lazy val CanEqualNullAnnot: ClassSymbol = requiredClass("scala.annotation.CanEqualNull")
992993

993994
@tu lazy val JavaRepeatableAnnot: ClassSymbol = requiredClass("java.lang.annotation.Repeatable")
994995

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,6 @@ object Mode {
129129
* Type `Null` becomes a subtype of non-primitive value types in TypeComparer.
130130
*/
131131
val RelaxedOverriding: Mode = newMode(30, "RelaxedOverriding")
132+
133+
val UnsafeJavaReturn: Mode = newMode(31, "UnsafeJavaReturn")
132134
}

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package dotty.tools.dotc
22
package core
33

4+
import Annotations._
45
import Contexts._
6+
import Symbols._
57
import Types._
68

79
/** Defines operations on nullable types and tree. */
@@ -42,6 +44,18 @@ object NullOpsDecorator:
4244
if ctx.explicitNulls then strip(self) else self
4345
}
4446

47+
def replaceOrNull(using Context): Type =
48+
def recur(tp: Type): Type = tp match
49+
case tp @ OrType(lhs, rhs) if rhs.isNullType =>
50+
AnnotatedType(recur(lhs), Annotation(defn.CanEqualNullAnnot))
51+
case tp: AndOrType =>
52+
tp.derivedAndOrType(recur(tp.tp1), recur(tp.tp2))
53+
case tp @ AppliedType(tycon, targs) =>
54+
tp.derivedAppliedType(tycon, targs.map(recur))
55+
case _ => tp
56+
if ctx.explicitNulls then recur(self) else self
57+
58+
4559
/** Is self (after widening and dealiasing) a type of the form `T | Null`? */
4660
def isNullableUnion(using Context): Boolean = {
4761
val stripped = self.stripNull

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,7 @@ object StdNames {
611611
val unapplySeq: N = "unapplySeq"
612612
val unbox: N = "unbox"
613613
val universe: N = "universe"
614+
val unsafeJavaReturn: N = "unsafeJavaReturn"
614615
val unsafeNulls: N = "unsafeNulls"
615616
val update: N = "update"
616617
val updateDynamic: N = "updateDynamic"

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import reporting._
2525
import transform.TypeUtils._
2626
import transform.SymUtils._
2727
import Nullables._
28+
import NullOpsDecorator._
2829
import config.Feature
2930

3031
import collection.mutable
@@ -908,7 +909,14 @@ trait Applications extends Compatibility {
908909
def simpleApply(fun1: Tree, proto: FunProto)(using Context): Tree =
909910
methPart(fun1).tpe match {
910911
case funRef: TermRef =>
911-
val app = ApplyTo(tree, fun1, funRef, proto, pt)
912+
var app = ApplyTo(tree, fun1, funRef, proto, pt)
913+
if ctx.mode.is(Mode.UnsafeJavaReturn) then
914+
val funSym = fun1.symbol
915+
if funSym.is(JavaDefined) && !funSym.isConstructor then
916+
val rtp1 = app.tpe
917+
val rtp2 = rtp1.replaceOrNull
918+
if rtp1 ne rtp2 then
919+
app = app.cast(rtp2)
912920
convertNewGenericArray(
913921
widenEnumCase(
914922
postProcessByNameArgs(funRef, app).computeNullable(),

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
143143
}
144144

145145
/** Is an `CanEqual[cls1, cls2]` instance assumed for predefined classes `cls1`, cls2`? */
146-
def canComparePredefinedClasses(cls1: ClassSymbol, cls2: ClassSymbol): Boolean =
146+
def canComparePredefinedClasses(cls1: ClassSymbol, cls2: ClassSymbol)(using Context): Boolean =
147147

148148
def cmpWithBoxed(cls1: ClassSymbol, cls2: ClassSymbol) =
149149
cls2 == defn.NothingClass
@@ -168,7 +168,8 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
168168
// val x: String = null.asInstanceOf[String]
169169
// if (x == null) {} // error: x is non-nullable
170170
// if (x.asInstanceOf[String|Null] == null) {} // ok
171-
cls1 == defn.NullClass && cls1 == cls2
171+
if cls1 == defn.NullClass then cls1 == cls2
172+
else cls1 == defn.NothingClass || cls2 == defn.NothingClass
172173
else if cls1 == defn.NullClass then
173174
cls1 == cls2 || cls2.derivesFrom(defn.ObjectClass)
174175
else if cls2 == defn.NullClass then
@@ -182,9 +183,12 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
182183
* interpret.
183184
*/
184185
def canComparePredefined(tp1: Type, tp2: Type) =
186+
val checkCtx = if ctx.explicitNulls
187+
&& (tp1.hasAnnotation(defn.CanEqualNullAnnot) || tp2.hasAnnotation(defn.CanEqualNullAnnot))
188+
then ctx.retractMode(Mode.SafeNulls) else ctx
185189
tp1.classSymbols.exists(cls1 =>
186190
tp2.classSymbols.exists(cls2 =>
187-
canComparePredefinedClasses(cls1, cls2)))
191+
canComparePredefinedClasses(cls1, cls2)(using checkCtx)))
188192

189193
formal.argTypes match
190194
case args @ (arg1 :: arg2 :: Nil) =>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package scala.annotation
2+
3+
final class CanEqualNull extends StaticAnnotation

library/src/scala/runtime/stdLibPatches/language.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ object language:
128128
@compileTimeOnly("`unsafeNulls` can only be used at compile time in import statements")
129129
object unsafeNulls
130130

131+
@compileTimeOnly("`unsafeJavaReturn` can only be used at compile time in import statements")
132+
object unsafeJavaReturn
133+
131134
@compileTimeOnly("`future` can only be used at compile time in import statements")
132135
object future
133136

0 commit comments

Comments
 (0)