Skip to content

Commit adc549b

Browse files
committed
Try to move the flexible types to outer level
1 parent 3aa4b0d commit adc549b

File tree

2 files changed

+32
-10
lines changed

2 files changed

+32
-10
lines changed

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

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,15 @@ object ImplicitNullInterop:
6565
assert(ctx.explicitNulls)
6666

6767
// Skip `TYPE`, enum values, and modules
68-
if isEnumValueDef || sym.name == nme.TYPE_ || sym.is(Flags.ModuleVal) then
68+
if isEnumValueDef
69+
|| sym.name == nme.TYPE_
70+
|| sym.name == nme.getClass_
71+
|| sym.name == nme.toString_
72+
|| sym.is(Flags.ModuleVal) then
6973
return tp
7074

7175
// Don't nullify result type for `toString`, constructors, and @NotNull methods
72-
val skipResultType = sym.name == nme.toString_ || sym.isConstructor || hasNotNullAnnot(sym)
76+
val skipResultType = sym.isConstructor || hasNotNullAnnot(sym)
7377
// Don't nullify Given/implicit parameters
7478
val skipCurrentLevel = sym.isOneOf(GivenOrImplicitVal)
7579

@@ -103,7 +107,7 @@ object ImplicitNullInterop:
103107
* The symbols are still under construction, so we don't have precise information.
104108
* We purposely do not rely on precise subtyping checks here (e.g., asking whether `tp <:< AnyRef`),
105109
* because doing so could force incomplete symbols or trigger cycles. Instead, we conservatively
106-
* nullify only when we can recognize a concrete reference type shape.
110+
* nullify only when we can recognize a concrete reference type or type parameters from Java.
107111
*/
108112
def needsNull(tp: Type): Boolean =
109113
if skipCurrentLevel || !tp.hasSimpleKind then false
@@ -149,7 +153,7 @@ object ImplicitNullInterop:
149153

150154
skipCurrentLevel = savedSkipCurrentLevel
151155
val appTp2 = derivedAppliedType(appTp, tycon, targs2)
152-
if tyconNeedsNull(tycon) then nullify(appTp2) else appTp2
156+
if tyconNeedsNull(tycon) && tp.hasSimpleKind then nullify(appTp2) else appTp2
153157
case ptp: PolyType =>
154158
derivedLambdaType(ptp)(ptp.paramInfos, this(ptp.resType))
155159
case mtp: MethodType =>
@@ -169,27 +173,43 @@ object ImplicitNullInterop:
169173
case tp: TypeBounds =>
170174
mapOver(tp)
171175
case tp: AndOrType =>
172-
// For unions/intersections we recurse into constituents but do not force an outer `| Null` here;
173-
// outer nullability is handled by the surrounding context. This keeps the result minimal and avoids
174-
// duplicating `| Null` on both sides and at the outer level.
175-
mapOver(tp)
176+
// For unions/intersections we recurse into both sides.
177+
// If both sides are nullalble, we only add `| Null` once.
178+
// This keeps the result minimal and avoids duplicating `| Null`
179+
// on both sides and at the outer level.
180+
(this(tp.tp1), this(tp.tp2)) match
181+
case (FlexibleType(_, t1), FlexibleType(_, t2)) if ctx.flexibleTypes =>
182+
FlexibleType(derivedAndOrType(tp, t1, t2))
183+
case (OrNull(t1), OrNull(t2)) =>
184+
OrNull(derivedAndOrType(tp, t1, t2))
185+
case (t1, t2) =>
186+
derivedAndOrType(tp, t1, t2)
176187
case tp: ExprType =>
177188
mapOver(tp)
178189
case tp: AnnotatedType =>
179190
// We don't nullify the annotation part.
180191
derivedAnnotatedType(tp, this(tp.underlying), tp.annot)
181192
case tp: RefinedType =>
182193
val savedSkipCurrentLevel = skipCurrentLevel
194+
val savedSkipResultType = skipResultType
183195

184-
// Nullify parent at outer level; not refined members
185196
skipCurrentLevel = true
186197
val parent2 = this(tp.parent)
187198

188199
skipCurrentLevel = false
200+
skipResultType = false
189201
val refinedInfo2 = this(tp.refinedInfo)
190202

191203
skipCurrentLevel = savedSkipCurrentLevel
192-
derivedRefinedType(tp, parent2, refinedInfo2)
204+
skipResultType = savedSkipResultType
205+
206+
parent2 match
207+
case FlexibleType(_, parent2a) if ctx.flexibleTypes =>
208+
FlexibleType(derivedRefinedType(tp, parent2a, refinedInfo2))
209+
case OrNull(parent2a) =>
210+
OrNull(derivedRefinedType(tp, parent2a, refinedInfo2))
211+
case _ =>
212+
derivedRefinedType(tp, parent2, refinedInfo2)
193213
case _ =>
194214
// In all other cases, return the type unchanged.
195215
// In particular, if the type is a ConstantType, then we don't nullify it because it is the

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6253,6 +6253,8 @@ object Types extends TypeUtils {
62536253
tp.derivedAndType(tp1, tp2)
62546254
protected def derivedOrType(tp: OrType, tp1: Type, tp2: Type): Type =
62556255
tp.derivedOrType(tp1, tp2)
6256+
protected def derivedAndOrType(tp: AndOrType, tp1: Type, tp2: Type): Type =
6257+
tp.derivedAndOrType(tp1, tp2)
62566258
protected def derivedMatchType(tp: MatchType, bound: Type, scrutinee: Type, cases: List[Type]): Type =
62576259
tp.derivedMatchType(bound, scrutinee, cases)
62586260
protected def derivedAnnotatedType(tp: AnnotatedType, underlying: Type, annot: Annotation): Type =

0 commit comments

Comments
 (0)