Skip to content

Commit 52d7b22

Browse files
committed
Do not flexify higher-kinded types
1 parent 099bf33 commit 52d7b22

File tree

6 files changed

+64
-11
lines changed

6 files changed

+64
-11
lines changed

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

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,19 +104,26 @@ object ImplicitNullInterop {
104104
def needsNull(tp: Type): Boolean =
105105
if outermostLevelAlreadyNullable then false
106106
else tp match
107+
case tp: TypeRef if !tp.hasSimpleKind => false
107108
case tp: TypeRef if
108109
// We don't modify value types because they're non-nullable even in Java.
109110
tp.symbol.isValueClass
110111
// We don't modify unit types.
111112
|| tp.isRef(defn.UnitClass)
112113
// We don't modify `Any` because it's already nullable.
113-
|| tp.isRef(defn.AnyClass)
114-
// We don't nullify Java varargs at the top level.
115-
// Example: if `setNames` is a Java method with signature `void setNames(String... names)`,
116-
// then its Scala signature will be `def setNames(names: (String|Null)*): Unit`.
117-
// This is because `setNames(null)` passes as argument a single-element array containing the value `null`,
118-
// and not a `null` array.
119-
|| !ctx.flexibleTypes && tp.isRef(defn.RepeatedParamClass) => false
114+
|| tp.isRef(defn.AnyClass) => false
115+
case _ => true
116+
117+
// We don't nullify Java varargs at the top level.
118+
// Example: if `setNames` is a Java method with signature `void setNames(String... names)`,
119+
// then its Scala signature will be `def setNames(names: (String|Null)*): Unit`.
120+
// This is because `setNames(null)` passes as argument a single-element array containing the value `null`,
121+
// and not a `null` array.
122+
def tyconNeedsNull(tp: Type): Boolean =
123+
if outermostLevelAlreadyNullable then false
124+
else tp match
125+
case tp: TypeRef
126+
if !ctx.flexibleTypes && tp.isRef(defn.RepeatedParamClass) => false
120127
case _ => true
121128

122129
override def apply(tp: Type): Type = tp match {
@@ -130,7 +137,7 @@ object ImplicitNullInterop {
130137
val targs2 = targs map this
131138
outermostLevelAlreadyNullable = oldOutermostNullable
132139
val appTp2 = derivedAppliedType(appTp, tycon, targs2)
133-
if needsNull(tycon) then nullify(appTp2) else appTp2
140+
if tyconNeedsNull(tycon) then nullify(appTp2) else appTp2
134141
case ptp: PolyType =>
135142
derivedLambdaType(ptp)(ptp.paramInfos, this(ptp.resType))
136143
case mtp: MethodType =>

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,6 +1830,8 @@ object Types extends TypeUtils {
18301830
t
18311831
case t @ SAMType(_, _) =>
18321832
t
1833+
case ft: FlexibleType =>
1834+
ft.underlying.findFunctionType
18331835
case _ =>
18341836
NoType
18351837

@@ -3401,15 +3403,17 @@ object Types extends TypeUtils {
34013403
override def underlying(using Context): Type = hi
34023404

34033405
def derivedFlexibleType(hi: Type)(using Context): Type =
3404-
if hi eq this.hi then this else FlexibleType(hi)
3406+
if hi eq this.hi then this else FlexibleType.make(hi)
34053407

34063408
override def computeHash(bs: Binders): Int = doHash(bs, hi)
34073409

34083410
override final def baseClasses(using Context): List[ClassSymbol] = hi.baseClasses
34093411
}
34103412

34113413
object FlexibleType {
3412-
def apply(tp: Type)(using Context): FlexibleType = tp match {
3414+
def apply(tp: Type)(using Context): FlexibleType =
3415+
// assert(tp.isValueType, s"Should not flexify ${tp}")
3416+
tp match {
34133417
case ft: FlexibleType => ft
34143418
case _ =>
34153419
// val tp1 = tp.stripNull()
@@ -3430,6 +3434,15 @@ object Types extends TypeUtils {
34303434
assert(!tp.isInstanceOf[LazyType])
34313435
FlexibleType(OrNull(tp), tp)
34323436
}
3437+
3438+
def make(tp: Type)(using Context): Type =
3439+
tp match
3440+
case _: FlexibleType => tp
3441+
case TypeBounds(lo, hi) => TypeBounds(FlexibleType.make(lo), FlexibleType.make(hi))
3442+
case wt: WildcardType => wt.optBounds match
3443+
case tb: TypeBounds => WildcardType(FlexibleType.make(tb).asInstanceOf[TypeBounds])
3444+
case _ => wt
3445+
case other => FlexibleType(tp)
34333446
}
34343447

34353448
// --- AndType/OrType ---------------------------------------------------------------

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,28 @@
11
import unsafeNulls.Foo.*
22
import unsafeNulls.Unsafe_1
33

4+
class Inherit_1 extends Unsafe_1 {
5+
override def foo(s: String): String = s
6+
override def bar[T >: String](s: T): T = s
7+
override def bar2[T >: String | Null](s: T): T = s
8+
}
9+
10+
class Inherit_2 extends Unsafe_1 {
11+
override def foo(s: String | Null): String | Null = null
12+
override def bar[T >: String](s: T | Null): T | Null = s
13+
override def bar2[T >: String](s: T): T = s
14+
}
15+
16+
class Inherit_3 extends Unsafe_1 {
17+
override def foo(s: String): String | Null = null
18+
override def bar[T >: String](s: T): T | Null = s
19+
}
20+
21+
class Inherit_4 extends Unsafe_1 {
22+
override def foo(s: String | Null): String = "non-null string"
23+
override def bar[T >: String](s: T | Null): T = "non-null string"
24+
}
25+
426
@main
527
def Flexible_2() =
628
val s2: String | Null = "foo"

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ class Unsafe_1 {
55
if (s == null) then "nullString"
66
else s
77
}
8+
def bar[T >: String](s: T): T = {
9+
???
10+
}
11+
def bar2[T >: String | Null](s: T): T = {
12+
???
13+
}
14+
def bar3[T <: Int => Int](g: T): T = g
815
}
916

1017
object Foo {

tests/explicit-nulls/pos/interop-sam-src/S.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@ def m = {
77

88
j.g1(f1)
99
j.g1((_: String | Null) => null)
10+
j.g1(_ => null)
11+
j.g1(x => if (x == "") null else null)
1012
j.g1(null)
1113

1214
j.g2(f2)
1315
j.g2((_: Int) => ())
16+
j.g2(_ => ())
17+
j.g2(x => if (x == 1) () else ())
1418
j.g2(null)
1519

1620
j.h1(f1)

0 commit comments

Comments
 (0)