Skip to content

Commit 352a5d0

Browse files
committed
Add Java Compatible Mode
1 parent 2080a0d commit 352a5d0

File tree

5 files changed

+46
-23
lines changed

5 files changed

+46
-23
lines changed

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ class ScalaSettings extends Settings.SettingGroup {
168168
// Extremely experimental language features
169169
val YnoKindPolymorphism: Setting[Boolean] = BooleanSetting("-Yno-kind-polymorphism", "Disable kind polymorphism.")
170170
val YexplicitNulls: Setting[Boolean] = BooleanSetting("-Yexplicit-nulls", "Make reference types non-nullable. Nullable types can be expressed with unions: e.g. String|Null.")
171+
val YexplicitNullsJavaCompatible: Setting[Boolean] = BooleanSetting("-Yexplicit-nulls-java", "explicit-nulls-java")
171172
val YerasedTerms: Setting[Boolean] = BooleanSetting("-Yerased-terms", "Allows the use of erased terms.")
172173
val YcheckInit: Setting[Boolean] = BooleanSetting("-Ycheck-init", "Check initialization of objects")
173174

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,8 @@ object Contexts {
451451
/** Is the explicit nulls option set? */
452452
def explicitNulls: Boolean = base.settings.YexplicitNulls.value
453453

454+
def explicitNullsJavaCompatible: Boolean = base.settings.YexplicitNullsJavaCompatible.value
455+
454456
protected def init(outer: Context, origin: Context): this.type = {
455457
util.Stats.record("Context.fresh")
456458
_outer = outer

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -730,10 +730,14 @@ object Types {
730730
go(l).meet(go(r), pre, safeIntersection = ctx.base.pendingMemberSearches.contains(name))
731731

732732
def goOr(tp: OrType) = tp match {
733-
case OrNull(tp1) if config.Feature.enabled(nme.unsafeNulls) =>
733+
case OrNull(tp1) if ctx.explicitNullsJavaCompatible || config.Feature.enabled(nme.unsafeNulls) =>
734734
// Selecting `name` from a type `T | Null` is like selecting `name` from `T`, if
735735
// unsafeNulls is enabled. This can throw at runtime, but we trade soundness for usability.
736-
tp1.findMember(name, pre.stripNull, required, excluded)
736+
val d = tp1.findMember(name, pre.stripNull, required, excluded)
737+
if config.Feature.enabled(nme.unsafeNulls) ||
738+
(ctx.explicitNullsJavaCompatible && d.symbol.is(JavaDefined))
739+
then d
740+
else go(tp.join)
737741
case _ =>
738742
// we need to keep the invariant that `pre <: tp`. Branch `union-types-narrow-prefix`
739743
// achieved that by narrowing `pre` to each alternative, but it led to merge errors in

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -889,7 +889,14 @@ trait Applications extends Compatibility {
889889
def simpleApply(fun1: Tree, proto: FunProto)(using Context): Tree =
890890
methPart(fun1).tpe match {
891891
case funRef: TermRef =>
892-
val app = ApplyTo(tree, fun1, funRef, proto, pt)
892+
val argsCtx =
893+
if ctx.explicitNulls && ctx.explicitNullsJavaCompatible then
894+
if funRef.symbol.is(JavaDefined) then
895+
ctx.addMode(Mode.UnsafeNullConversion)
896+
else
897+
ctx.retractMode(Mode.UnsafeNullConversion)
898+
else ctx
899+
val app = ApplyTo(tree, fun1, funRef, proto, pt)(using argsCtx)
893900
convertNewGenericArray(
894901
postProcessByNameArgs(funRef, app).computeNullable())
895902
case _ =>

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

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3470,12 +3470,11 @@ class Typer extends Namer
34703470
}
34713471
}
34723472

3473-
def tryUnsafeNullConver(fail: => Tree): Tree =
3473+
def tryUnsafeNullConver(fail: => Tree)(using Context): Tree =
34743474
// If explicitNulls and unsafeNulls are enabled, and
3475-
if ctx.explicitNulls && config.Feature.enabled(nme.unsafeNulls) &&
3476-
tree.tpe.isUnsafeConvertable(pt) then {
3477-
tree.cast(pt)
3478-
}
3475+
if ctx.mode.is(Mode.UnsafeNullConversion) && pt.isValueType &&
3476+
tree.tpe.isUnsafeConvertable(pt)
3477+
then tree.cast(pt)
34793478
else fail
34803479

34813480
def cannotFind(failure: SearchFailure) =
@@ -3488,21 +3487,31 @@ class Typer extends Namer
34883487
tree
34893488
else recover(failure.reason)
34903489

3491-
if ctx.mode.is(Mode.ImplicitsEnabled) && tree.typeOpt.isValueType then
3492-
if pt.isRef(defn.AnyValClass) || pt.isRef(defn.ObjectClass) then
3493-
ctx.error(em"the result of an implicit conversion must be more specific than $pt", tree.sourcePos)
3494-
val searchCtx = if config.Feature.enabled(nme.unsafeNulls)
3495-
then ctx.addMode(Mode.UnsafeNullConversion)
3496-
else ctx
3497-
tree.tpe match {
3498-
case OrNull(tpe1) if ctx.explicitNulls && config.Feature.enabled(nme.unsafeNulls) =>
3499-
searchTree(tree.cast(tpe1)) { _ =>
3500-
searchTree(tree)(failure => tryUnsafeNullConver(cannotFind(failure)))(using searchCtx)
3501-
}(using searchCtx)
3502-
case _ =>
3503-
searchTree(tree)(failure => tryUnsafeNullConver(cannotFind(failure)))(using searchCtx)
3504-
}
3505-
else tryUnsafeNullConver(recover(NoMatchingImplicits))
3490+
def process(using Context): Tree = {
3491+
if ctx.mode.is(Mode.ImplicitsEnabled) && tree.typeOpt.isValueType then
3492+
if pt.isRef(defn.AnyValClass) || pt.isRef(defn.ObjectClass) then
3493+
ctx.error(em"the result of an implicit conversion must be more specific than $pt", tree.sourcePos)
3494+
tree.tpe match {
3495+
case OrNull(tpe1) if ctx.explicitNulls && config.Feature.enabled(nme.unsafeNulls) =>
3496+
searchTree(tree.cast(tpe1)) { _ =>
3497+
searchTree(tree)(failure => tryUnsafeNullConver(cannotFind(failure)))
3498+
}
3499+
case _ =>
3500+
searchTree(tree)(failure => tryUnsafeNullConver(cannotFind(failure)))
3501+
}
3502+
else tryUnsafeNullConver(recover(NoMatchingImplicits))
3503+
}
3504+
3505+
val javaCompatibleCall = ctx.explicitNullsJavaCompatible && (tree match {
3506+
case Apply(_, _) => tree.symbol.is(JavaDefined)
3507+
case _ => false
3508+
})
3509+
val searchCtx =
3510+
if ctx.explicitNulls && (javaCompatibleCall || config.Feature.enabled(nme.unsafeNulls)) then
3511+
ctx.addMode(Mode.UnsafeNullConversion)
3512+
else ctx
3513+
3514+
process(using searchCtx)
35063515
}
35073516

35083517
def adaptType(tp: Type): Tree = {

0 commit comments

Comments
 (0)