Skip to content

Commit b68ed5c

Browse files
committed
Fix checkImplicitUse
1 parent 2899ddc commit b68ed5c

File tree

9 files changed

+48
-42
lines changed

9 files changed

+48
-42
lines changed

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

+5-5
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ class Definitions {
456456
ScalaPackageClass, tpnme.Nothing, AbstractFinal, List(AnyClass.typeRef))
457457
def NothingType: TypeRef = NothingClass.typeRef
458458
@tu lazy val NullClass: ClassSymbol = {
459-
val parent = if (ctx.explicitNulls) AnyType else ObjectType
459+
val parent = if ctx.explicitNulls then AnyType else ObjectType
460460
enterCompleteClassSymbol(ScalaPackageClass, tpnme.Null, AbstractFinal, parent :: Nil)
461461
}
462462
def NullType: TypeRef = NullClass.typeRef
@@ -640,14 +640,14 @@ class Definitions {
640640
@tu lazy val ClassCastExceptionClass: ClassSymbol = requiredClass("java.lang.ClassCastException")
641641
@tu lazy val ClassCastExceptionClass_stringConstructor: TermSymbol = ClassCastExceptionClass.info.member(nme.CONSTRUCTOR).suchThat(_.info.firstParamTypes match {
642642
case List(pt) =>
643-
val pt1 = if (ctx.explicitNulls) pt.stripNull else pt
643+
val pt1 = if ctx.explicitNulls then pt.stripNull else pt
644644
pt1.isRef(StringClass)
645645
case _ => false
646646
}).symbol.asTerm
647647
@tu lazy val ArithmeticExceptionClass: ClassSymbol = requiredClass("java.lang.ArithmeticException")
648648
@tu lazy val ArithmeticExceptionClass_stringConstructor: TermSymbol = ArithmeticExceptionClass.info.member(nme.CONSTRUCTOR).suchThat(_.info.firstParamTypes match {
649649
case List(pt) =>
650-
val pt1 = if (ctx.explicitNulls) pt.stripNull else pt
650+
val pt1 = if ctx.explicitNulls then pt.stripNull else pt
651651
pt1.isRef(StringClass)
652652
case _ => false
653653
}).symbol.asTerm
@@ -1163,13 +1163,13 @@ class Definitions {
11631163
idx == name.length || name(idx).isDigit && digitsOnlyAfter(name, idx + 1)
11641164

11651165
def isBottomClass(cls: Symbol): Boolean =
1166-
if (ctx.explicitNulls && !ctx.phase.erasedTypes) cls == NothingClass
1166+
if ctx.explicitNulls && !ctx.phase.erasedTypes then cls == NothingClass
11671167
else isBottomClassAfterErasure(cls)
11681168

11691169
def isBottomClassAfterErasure(cls: Symbol): Boolean = cls == NothingClass || cls == NullClass
11701170

11711171
def isBottomType(tp: Type): Boolean =
1172-
if (ctx.explicitNulls && !ctx.phase.erasedTypes) tp.derivesFrom(NothingClass)
1172+
if ctx.explicitNulls && !ctx.phase.erasedTypes then tp.derivesFrom(NothingClass)
11731173
else isBottomTypeAfterErasure(tp)
11741174

11751175
def isBottomTypeAfterErasure(tp: Type): Boolean =

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ object JavaNullInterop {
5858
assert(sym.is(JavaDefined), "can only nullify java-defined members")
5959

6060
// Some special cases when nullifying the type
61-
if (isEnumValueDef || sym.name == nme.TYPE_)
61+
if isEnumValueDef || sym.name == nme.TYPE_ then
6262
// Don't nullify the `TYPE` field in every class and Java enum instances
6363
tp
64-
else if (sym.name == nme.toString_ || sym.isConstructor || hasNotNullAnnot(sym))
64+
else if sym.name == nme.toString_ || sym.isConstructor || hasNotNullAnnot(sym) then
6565
// Don't nullify the return type of the `toString` method.
6666
// Don't nullify the return type of constructors.
6767
// Don't nullify the return type of methods with a not-null annotation.
@@ -126,7 +126,7 @@ object JavaNullInterop {
126126
val targs2 = targs map this
127127
outermostLevelAlreadyNullable = oldOutermostNullable
128128
val appTp2 = derivedAppliedType(appTp, tycon, targs2)
129-
if (needsNull(tycon)) OrNull(appTp2) else appTp2
129+
if needsNull(tycon) then OrNull(appTp2) else appTp2
130130
case ptp: PolyType =>
131131
derivedLambdaType(ptp)(ptp.paramInfos, this(ptp.resType))
132132
case mtp: MethodType =>

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

+9-5
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,17 @@ object NullOpsDecorator {
7272
def isUnsafelyNulltoAnyRef(pt: Type)(using Context): Boolean =
7373
self.isNullType && pt.isNullableAfterErasure
7474

75+
def isUnsafeSubtype(pt: Type, relaxedSubtype: Boolean = false)(using Context): Boolean =
76+
val selfs = self.stripAllNulls
77+
val pts = pt.stripAllNulls
78+
if relaxedSubtype then
79+
selfs relaxed_<:< pts
80+
else
81+
selfs <:< pts
82+
7583
/** Can we convert a tree with type `self` to type `pt` unsafely.
7684
*/
7785
def isUnsafelyConvertable(pt: Type, relaxedSubtype: Boolean = false)(using Context): Boolean =
78-
self.isUnsafelyNulltoAnyRef(pt) ||
79-
(if relaxedSubtype then
80-
self.stripAllNulls relaxed_<:< pt.stripAllNulls
81-
else
82-
self.stripAllNulls <:< pt.stripAllNulls)
86+
self.isUnsafelyNulltoAnyRef(pt) || self.isUnsafeSubtype(pt, relaxedSubtype)
8387
}
8488
}

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

+6-7
Original file line numberDiff line numberDiff line change
@@ -515,13 +515,12 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
515515
}
516516

517517
private def eraseArray(tp: Type)(using Context) = {
518-
val defn.ArrayOf(elemtp) = tp
519-
if (classify(elemtp).derivesFrom(defn.NullClass)) JavaArrayType(defn.ObjectType)
520-
else {
521-
val elemtp1 = if (ctx.explicitNulls) elemtp.stripNull else elemtp
522-
if (isUnboundedGeneric(elemtp1) && !isJava) defn.ObjectType
523-
else JavaArrayType(erasureFn(isJava, semiEraseVCs = false, isConstructor, wildcardOK)(elemtp1))
524-
}
518+
val defn.ArrayOf(elemtp0) = tp
519+
val elemtp = if ctx.explicitNulls then elemtp0.stripNull else elemtp0
520+
if classify(elemtp).derivesFrom(defn.NullClass) then JavaArrayType(defn.ObjectType)
521+
else
522+
if isUnboundedGeneric(elemtp) && !isJava then defn.ObjectType
523+
else JavaArrayType(erasureFn(isJava, semiEraseVCs = false, isConstructor, wildcardOK)(elemtp))
525524
}
526525

527526
private def erasePair(tp: Type)(using Context): Type = {

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
201201
// Second constructor of ioob that takes a String argument
202202
def filterStringConstructor(s: Symbol): Boolean = s.info match {
203203
case m: MethodType if s.isConstructor && m.paramInfos.size == 1 =>
204-
val pinfo = if (ctx.explicitNulls) m.paramInfos.head.stripNull else m.paramInfos.head
204+
val head = m.paramInfos.head
205+
val pinfo = if ctx.explicitNulls then head.stripNull else head
205206
pinfo == defn.StringType
206207
case _ => false
207208
}

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

+7-3
Original file line numberDiff line numberDiff line change
@@ -767,16 +767,20 @@ trait Checking {
767767
* enabled.
768768
*/
769769
def checkImplicitConversionUseOK(tree: Tree)(using Context): Unit =
770-
val sym = tree.symbol
770+
val tree1 = if ctx.mode.is(Mode.UnsafeNullConversion) then
771+
// If unsafeNulls is enabled, a cast and a closure could be added to the original tree
772+
stripCast(closureBody(tree))
773+
else tree
774+
val sym = tree1.symbol
771775
if sym.name == nme.apply
772776
&& sym.owner.derivesFrom(defn.ConversionClass)
773777
&& !sym.info.isErroneous
774778
then
775-
def conv = methPart(tree) match
779+
def conv = methPart(tree1) match
776780
case Select(qual, _) => qual.symbol.orElse(sym.owner)
777781
case _ => sym.owner
778782
checkFeature(nme.implicitConversions,
779-
i"Use of implicit conversion ${conv.showLocated}", NoSymbol, tree.srcPos)
783+
i"Use of implicit conversion ${conv.showLocated}", NoSymbol, tree1.srcPos)
780784

781785
private def infixOKSinceFollowedBy(tree: untpd.Tree): Boolean = tree match {
782786
case _: untpd.Block | _: untpd.Match => true

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ object Implicits:
133133
if (approx) formal = wildApprox(formal)
134134
explore((argType relaxed_<:< formal.widenExpr) ||
135135
Nullables.convertUnsafeNulls &&
136-
argType.isUnsafelyConvertable(formal.widenExpr))
136+
argType.isUnsafeSubtype(formal.widenExpr, true))
137137
})
138138
Candidate.Conversion
139139
else

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

+3-5
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,8 @@ object ProtoTypes {
4040
val tpw = tp.widenExpr
4141
val ptw = pt.widenExpr
4242
(tpw relaxed_<:< ptw) ||
43-
// If unsafeNulls is enabled, we relax the condition by striping all nulls from the types
44-
// before subtype check. We use Feature to check language feature. However, when we search implicits,
45-
// the context is from ContextualImplicits; hence, we don't know whether unsafeNulls is enabled.
46-
// We have to add Mode.UnsafeNullConversion before implicit search.
43+
// If unsafeNulls is enabled, we relax the condition by
44+
// striping all nulls from the types before subtype check.
4745
Nullables.convertUnsafeNulls && tpw.isUnsafelyConvertable(ptw, true) ||
4846
viewExists(tp, pt)
4947

@@ -55,9 +53,9 @@ object ProtoTypes {
5553
val ptw = pt.widenExpr
5654
necessarySubType(tpw, ptw) || tpw.isValueSubType(ptw) ||
5755
Nullables.convertUnsafeNulls && {
56+
// See comments in `isCompatible`
5857
val tpwsn = tpw.stripAllNulls
5958
val ptwsn = ptw.stripAllNulls
60-
// See comments in `isCompatible`
6159
necessarySubType(tpwsn, ptwsn) || tpwsn.isValueSubType(ptwsn) ||
6260
tpwsn.isUnsafelyNulltoAnyRef(ptwsn)
6361
} ||

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

+12-12
Original file line numberDiff line numberDiff line change
@@ -556,18 +556,18 @@ class Typer extends Namer
556556

557557
def typeSelectOnTerm(using Context): Tree =
558558
val qual = typedExpr(tree.qualifier, selectionProto(tree.name, pt, this))
559-
val qual1 = qual.tpe match {
560-
case OrNull(tpe1) if config.Feature.enabled(nme.unsafeNulls) =>
561-
qual.cast(AndType(qual.tpe, tpe1))
562-
case tp =>
563-
if ctx.explicitNulls &&
564-
tp.isNullType &&
565-
config.Feature.enabled(nme.unsafeNulls) &&
566-
(tree.name == nme.eq || tree.name == nme.ne) then
567-
// Allow selecting `eq` and `ne` on `Null` specially
568-
qual.cast(defn.ObjectType)
569-
else qual
570-
}
559+
val qual1 = if Nullables.convertUnsafeNulls then
560+
qual.tpe match {
561+
case OrNull(tpe1) =>
562+
qual.cast(AndType(qual.tpe, tpe1))
563+
case tp =>
564+
if tp.isNullType &&
565+
(tree.name == nme.eq || tree.name == nme.ne) then
566+
// Allow selecting `eq` and `ne` on `Null` specially
567+
qual.cast(defn.ObjectType)
568+
else qual
569+
}
570+
else qual
571571
typedSelect(tree, pt, qual1).computeNullable()
572572

573573
def typeSelectOnType(qual: untpd.Tree)(using Context) =

0 commit comments

Comments
 (0)