Skip to content

Commit 6e20228

Browse files
committed
Add Java Compatible Mode
1 parent 41aaee4 commit 6e20228

File tree

6 files changed

+47
-24
lines changed

6 files changed

+47
-24
lines changed

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

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

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

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

468+
def explicitNullsJavaCompatible: Boolean = base.settings.YexplicitNullsJavaCompatible.value
469+
468470
/** Initialize all context fields, except typerState, which has to be set separately
469471
* @param outer The outer context
470472
* @param origin The context from which fields are copied

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

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

740740
def goOr(tp: OrType) = tp match {
741-
case OrNull(tp1) if config.Feature.enabled(nme.unsafeNulls) =>
741+
case OrNull(tp1) if ctx.explicitNullsJavaCompatible || config.Feature.enabled(nme.unsafeNulls) =>
742742
// Selecting `name` from a type `T | Null` is like selecting `name` from `T`, if
743743
// unsafeNulls is enabled. This can throw at runtime, but we trade soundness for usability.
744-
tp1.findMember(name, pre.stripNull, required, excluded)
744+
val d = tp1.findMember(name, pre.stripNull, required, excluded)
745+
if config.Feature.enabled(nme.unsafeNulls) ||
746+
(ctx.explicitNullsJavaCompatible && d.symbol.is(JavaDefined))
747+
then d
748+
else go(tp.join)
745749
case _ =>
746750
// we need to keep the invariant that `pre <: tp`. Branch `union-types-narrow-prefix`
747751
// 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
@@ -888,7 +888,14 @@ trait Applications extends Compatibility {
888888
def simpleApply(fun1: Tree, proto: FunProto)(using Context): Tree =
889889
methPart(fun1).tpe match {
890890
case funRef: TermRef =>
891-
val app = ApplyTo(tree, fun1, funRef, proto, pt)
891+
val argsCtx =
892+
if ctx.explicitNulls && ctx.explicitNullsJavaCompatible then
893+
if funRef.symbol.is(JavaDefined) then
894+
ctx.addMode(Mode.UnsafeNullConversion)
895+
else
896+
ctx.retractMode(Mode.UnsafeNullConversion)
897+
else ctx
898+
val app = ApplyTo(tree, fun1, funRef, proto, pt)(using argsCtx)
892899
convertNewGenericArray(
893900
postProcessByNameArgs(funRef, app).computeNullable())
894901
case _ =>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ trait Checking {
782782
conv.name == nme.reflectiveSelectable && conv.maybeOwner.maybeOwner.maybeOwner == defn.ScalaPackageClass
783783
if (!conversionOK)
784784
checkFeature(nme.implicitConversions,
785-
i"Use of implicit conversion ${conv.showLocated}", NoSymbol, pos)
785+
i"Use of implicit conversion ${conv.showLocated}", NoSymbol, posd)
786786
}
787787

788788
private def infixOKSinceFollowedBy(tree: untpd.Tree): Boolean = tree match {

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

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3509,12 +3509,11 @@ class Typer extends Namer
35093509
}
35103510
}
35113511

3512-
def tryUnsafeNullConver(fail: => Tree): Tree =
3512+
def tryUnsafeNullConver(fail: => Tree)(using Context): Tree =
35133513
// If explicitNulls and unsafeNulls are enabled, and
3514-
if ctx.explicitNulls && config.Feature.enabled(nme.unsafeNulls) &&
3515-
tree.tpe.isUnsafeConvertable(pt) then {
3516-
tree.cast(pt)
3517-
}
3514+
if ctx.mode.is(Mode.UnsafeNullConversion) && pt.isValueType &&
3515+
tree.tpe.isUnsafeConvertable(pt)
3516+
then tree.cast(pt)
35183517
else fail
35193518

35203519
def cannotFind(failure: SearchFailure) =
@@ -3527,21 +3526,31 @@ class Typer extends Namer
35273526
tree
35283527
else recover(failure.reason)
35293528

3530-
if ctx.mode.is(Mode.ImplicitsEnabled) && tree.typeOpt.isValueType then
3531-
if pt.isRef(defn.AnyValClass) || pt.isRef(defn.ObjectClass) then
3532-
ctx.error(em"the result of an implicit conversion must be more specific than $pt", tree.sourcePos)
3533-
val searchCtx = if config.Feature.enabled(nme.unsafeNulls)
3534-
then ctx.addMode(Mode.UnsafeNullConversion)
3535-
else ctx
3536-
tree.tpe match {
3537-
case OrNull(tpe1) if ctx.explicitNulls && config.Feature.enabled(nme.unsafeNulls) =>
3538-
searchTree(tree.cast(tpe1)) { _ =>
3539-
searchTree(tree)(failure => tryUnsafeNullConver(cannotFind(failure)))(using searchCtx)
3540-
}(using searchCtx)
3541-
case _ =>
3542-
searchTree(tree)(failure => tryUnsafeNullConver(cannotFind(failure)))(using searchCtx)
3543-
}
3544-
else tryUnsafeNullConver(recover(NoMatchingImplicits))
3529+
def process(using Context): Tree = {
3530+
if ctx.mode.is(Mode.ImplicitsEnabled) && tree.typeOpt.isValueType then
3531+
if pt.isRef(defn.AnyValClass) || pt.isRef(defn.ObjectClass) then
3532+
report.error(em"the result of an implicit conversion must be more specific than $pt", tree.srcPos)
3533+
tree.tpe match {
3534+
case OrNull(tpe1) if ctx.explicitNulls && config.Feature.enabled(nme.unsafeNulls) =>
3535+
searchTree(tree.cast(tpe1)) { _ =>
3536+
searchTree(tree)(failure => tryUnsafeNullConver(cannotFind(failure)))
3537+
}
3538+
case _ =>
3539+
searchTree(tree)(failure => tryUnsafeNullConver(cannotFind(failure)))
3540+
}
3541+
else tryUnsafeNullConver(recover(NoMatchingImplicits))
3542+
}
3543+
3544+
val javaCompatibleCall = ctx.explicitNullsJavaCompatible && (tree match {
3545+
case Apply(_, _) => tree.symbol.is(JavaDefined)
3546+
case _ => false
3547+
})
3548+
val searchCtx =
3549+
if ctx.explicitNulls && (javaCompatibleCall || config.Feature.enabled(nme.unsafeNulls)) then
3550+
ctx.addMode(Mode.UnsafeNullConversion)
3551+
else ctx
3552+
3553+
process(using searchCtx)
35453554
}
35463555

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

0 commit comments

Comments
 (0)