Skip to content

Commit ac266a0

Browse files
committed
Fix bounds realizability check and vargs
1 parent 290d9b6 commit ac266a0

File tree

6 files changed

+187
-134
lines changed

6 files changed

+187
-134
lines changed

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import Denotations.SingleDenotation
77
import Decorators._
88
import collection.mutable
99
import config.SourceVersion.`3.1`
10-
import config.Feature.sourceVersion
10+
import config.Feature.{sourceVersion, unsafeNullsEnabled}
11+
import typer.Nullables
1112

1213
/** Realizability status */
1314
object CheckRealizable {
@@ -160,10 +161,14 @@ class CheckRealizable(using Context) {
160161
*/
161162
private def boundsRealizability(tp: Type) = {
162163

164+
val unsafeNullsSub = unsafeNullsEnabled
165+
def isSub(tp1: Type, tp2: Type): Boolean =
166+
Nullables.useUnsafeNullsSubTypeIf(unsafeNullsSub)(tp1 <:< tp2)
167+
163168
val memberProblems = withMode(Mode.CheckBounds) {
164169
for {
165170
mbr <- tp.nonClassTypeMembers
166-
if !(mbr.info.loBound <:< mbr.info.hiBound)
171+
if !isSub(mbr.info.loBound, mbr.info.hiBound)
167172
}
168173
yield new HasProblemBounds(mbr.name, mbr.info)
169174
}
@@ -173,7 +178,7 @@ class CheckRealizable(using Context) {
173178
name <- refinedNames(tp)
174179
if (name.isTypeName)
175180
mbr <- tp.member(name).alternatives
176-
if !(mbr.info.loBound <:< mbr.info.hiBound)
181+
if !isSub(mbr.info.loBound, mbr.info.hiBound)
177182
}
178183
yield
179184
new HasProblemBounds(name, mbr.info)
@@ -184,7 +189,7 @@ class CheckRealizable(using Context) {
184189
new HasProblemBase(base1, base2) :: Nil
185190
case base =>
186191
base.argInfos.collect {
187-
case bounds @ TypeBounds(lo, hi) if !(lo <:< hi) =>
192+
case bounds @ TypeBounds(lo, hi) if !isSub(lo, hi) =>
188193
new HasProblemBaseArg(base, bounds)
189194
}
190195
}

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
package dotty.tools.dotc.core
1+
package dotty.tools.dotc
2+
package core
23

3-
import Contexts.{Context, ctx}
4+
import ast.Trees._
5+
import Contexts._
46
import Symbols.defn
57
import Types._
68

7-
/** Defines operations on nullable types. */
9+
/** Defines operations on nullable types and tree. */
810
object NullOpsDecorator {
911

1012
extension (self: Type) {
@@ -76,4 +78,13 @@ object NullOpsDecorator {
7678
|| !self.isNothingType
7779
&& self.derivesFrom(defn.ObjectClass, isErased = true)
7880
}
81+
82+
import ast.tpd._
83+
84+
extension (self: Tree) {
85+
def castToNonNullable(using Context): Tree = self.typeOpt match {
86+
case OrNull(tp) => self.cast(tp)
87+
case _ => self
88+
}
89+
}
7990
}

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

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ import Decorators._
1010
import util.Stats._
1111
import Names._
1212
import NameOps._
13+
import NullOpsDecorator._
1314
import Flags.Module
1415
import Variances.variancesConform
1516
import dotty.tools.dotc.config.Config
17+
import config.Feature.unsafeNullsEnabled
1618

1719
object TypeApplications {
1820

@@ -400,21 +402,26 @@ class TypeApplications(val self: Type) extends AnyVal {
400402
* `from` and `to` must be static classes, both with one type parameter, and the same variance.
401403
* Do the same for by name types => From[T] and => To[T]
402404
*/
403-
def translateParameterized(from: ClassSymbol, to: ClassSymbol, wildcardArg: Boolean = false)(using Context): Type = self match {
405+
def translateParameterized(from: ClassSymbol, to: ClassSymbol, wildcardArg: Boolean = false, withOrNull: Boolean = false)(using Context): Type = self match {
404406
case self @ ExprType(tp) =>
405407
self.derivedExprType(tp.translateParameterized(from, to))
406408
case _ =>
407-
if (self.derivesFrom(from)) {
409+
if (self.derivesFrom(from, isErased = unsafeNullsEnabled || withOrNull)) {
410+
def isBottom(t: Type): Boolean =
411+
if unsafeNullsEnabled || withOrNull
412+
then t.isBottomTypeAfterErasure
413+
else t.isBottomType
408414
def elemType(tp: Type): Type = tp.widenDealias match
409415
case tp: OrType =>
410-
if tp.tp1.isBottomType then elemType(tp.tp2)
411-
else if tp.tp2.isBottomType then elemType(tp.tp1)
416+
if isBottom(tp.tp1) then elemType(tp.tp2)
417+
else if isBottom(tp.tp2) then elemType(tp.tp1)
412418
else tp.derivedOrType(elemType(tp.tp1), elemType(tp.tp2))
413419
case tp: AndType => tp.derivedAndType(elemType(tp.tp1), elemType(tp.tp2))
414420
case _ => tp.baseType(from).argInfos.headOption.getOrElse(defn.NothingType)
415421
val arg = elemType(self)
416-
val arg1 = if (wildcardArg) TypeBounds.upper(arg) else arg
417-
to.typeRef.appliedTo(arg1)
422+
val arg1 = if withOrNull && arg.isNullableAfterErasure then OrNull(arg) else arg
423+
val arg2 = if (wildcardArg) TypeBounds.upper(arg1) else arg1
424+
to.typeRef.appliedTo(arg2)
418425
}
419426
else self
420427
}
@@ -425,14 +432,14 @@ class TypeApplications(val self: Type) extends AnyVal {
425432
* will be translated to `*<?>`.
426433
* Other types are kept as-is.
427434
*/
428-
def translateFromRepeated(toArray: Boolean, translateWildcard: Boolean = false)(using Context): Type =
435+
def translateFromRepeated(toArray: Boolean, translateWildcard: Boolean = false, withOrNull: Boolean = false)(using Context): Type =
429436
val seqClass = if (toArray) defn.ArrayClass else defn.SeqClass
430437
if translateWildcard && self.isInstanceOf[WildcardType] then
431438
seqClass.typeRef.appliedTo(WildcardType)
432439
else if self.isRepeatedParam then
433440
// We want `Array[? <: T]` because arrays aren't covariant until after
434441
// erasure. See `tests/pos/i5140`.
435-
translateParameterized(defn.RepeatedParamClass, seqClass, wildcardArg = toArray)
442+
translateParameterized(defn.RepeatedParamClass, seqClass, wildcardArg = toArray, withOrNull = withOrNull)
436443
else self
437444

438445
/** Translate a `From[T]` into a `*T`. */

compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import Decorators._
1414
import Denotations._, SymDenotations._
1515
import TypeErasure.erasure
1616
import DenotTransformers._
17+
import NullOpsDecorator._
1718

1819
object ElimRepeated {
1920
val name: String = "elimRepeated"
@@ -154,7 +155,8 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
154155
case SeqLiteral(elems, elemtpt) =>
155156
JavaSeqLiteral(elems, elemtpt)
156157
case _ =>
157-
val elemType = tree.tpe.elemType
158+
// TODO remove Null from the type first
159+
val elemType = tree.tpe.stripNull.elemType
158160
var elemClass = erasure(elemType).classSymbol
159161
if defn.NotRuntimeClasses.contains(elemClass) then
160162
elemClass = defn.ObjectClass
@@ -171,7 +173,8 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase =>
171173
* of generic Java varargs in `elimRepeated`.
172174
*/
173175
private def adaptToArray(tree: Tree, elemPt: Type)(implicit ctx: Context): Tree =
174-
val elemTp = tree.tpe.elemType
176+
// TODO remove Null from the type first
177+
val elemTp = tree.tpe.stripNull.elemType
175178
val elemTpMatches = elemTp <:< elemPt
176179
val treeIsArray = tree.tpe.derivesFrom(defn.ArrayClass)
177180
if elemTpMatches && treeIsArray then

0 commit comments

Comments
 (0)