Skip to content

Commit e08d48b

Browse files
committed
Merge chnages from other PRs
1 parent ccfbb63 commit e08d48b

File tree

9 files changed

+69
-96
lines changed

9 files changed

+69
-96
lines changed

compiler/src/dotty/tools/dotc/ast/TreeMapWithImplicits.scala

+1-7
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,7 @@ class TreeMapWithImplicits extends tpd.TreeMapWithPreciseStatContexts {
5050
override def transform(tree: Tree)(using Context): Tree = {
5151
try tree match {
5252
case Block(stats, expr) =>
53-
inContext(nestedScopeCtx(stats)) {
54-
if stats.exists(_.isInstanceOf[Import]) then
55-
// need to transform stats and expr together to account for import visibility
56-
val stats1 = transformStats(stats :+ expr, ctx.owner)
57-
cpy.Block(tree)(stats1.init, stats1.last)
58-
else super.transform(tree)
59-
}
53+
super.transform(tree)(using nestedScopeCtx(stats))
6054
case tree: DefDef =>
6155
inContext(localCtx(tree)) {
6256
cpy.DefDef(tree)(

compiler/src/dotty/tools/dotc/ast/tpd.scala

+7
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,13 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
12011201
* - imports are reflected in the contexts of subsequent statements
12021202
*/
12031203
class TreeMapWithPreciseStatContexts(cpy: TreeCopier = tpd.cpy) extends TreeMap(cpy):
1204+
override def transform(tree: Tree)(using Context): Tree = tree match
1205+
case Block(stats, expr) =>
1206+
val stats1 = transformStats(stats :+ expr, ctx.owner)
1207+
cpy.Block(tree)(stats1.init, stats1.last)
1208+
case _ =>
1209+
super.transform(tree)
1210+
12041211
override def transformStats(trees: List[Tree], exprOwner: Symbol)(using Context): List[Tree] =
12051212
trees.mapStatements(exprOwner, transform(_))
12061213

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

+5
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,9 @@ object Mode {
126126
* This mode forces expansion of inline calls in those positions even during typing.
127127
*/
128128
val ForceInline: Mode = newMode(29, "ForceInline")
129+
130+
/** This mode is enabled when we check Java overriding in explicit nulls.
131+
* Type `Null` becomes a bottom type in TypeComparer.
132+
*/
133+
val RelaxedOverriding: Mode = newMode(30, "RelaxedOverriding")
129134
}

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

+25-48
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package dotty.tools.dotc
22
package core
33

4-
import scala.language.{unsafeNulls => _}
5-
64
import ast.Trees._
75
import Contexts._
86
import Symbols.defn
@@ -11,43 +9,6 @@ import Types._
119
/** Defines operations on nullable types and tree. */
1210
object NullOpsDecorator:
1311

14-
private class StripNullsMap(isDeep: Boolean)(using Context) extends TypeMap:
15-
def strip(tp: Type): Type = tp match
16-
case tp @ OrType(lhs, rhs) =>
17-
val llhs = this(lhs)
18-
val rrhs = this(rhs)
19-
if rrhs.isNullType then llhs
20-
else if llhs.isNullType then rrhs
21-
else derivedOrType(tp, llhs, rrhs)
22-
case tp @ AndType(tp1, tp2) =>
23-
// We cannot `tp.derivedAndType(strip(tp1), strip(tp2))` directly,
24-
// since `stripNull((A | Null) & B)` would produce the wrong
25-
// result `(A & B) | Null`.
26-
val tp1s = this(tp1)
27-
val tp2s = this(tp2)
28-
if isDeep || (tp1s ne tp1) && (tp2s ne tp2) then
29-
derivedAndType(tp, tp1s, tp2s)
30-
else tp
31-
case tp: TypeBounds =>
32-
mapOver(tp)
33-
case _ => tp
34-
35-
def stripOver(tp: Type): Type = tp match
36-
case appTp @ AppliedType(tycon, targs) =>
37-
derivedAppliedType(appTp, tycon, targs.map(this))
38-
case ptp: PolyType =>
39-
derivedLambdaType(ptp)(ptp.paramInfos, this(ptp.resType))
40-
case mtp: MethodType =>
41-
mapOver(mtp)
42-
case _ => strip(tp)
43-
44-
override def apply(tp: Type): Type =
45-
val tpw = tp.widenDealias
46-
val tpws = if isDeep then stripOver(tpw) else strip(tpw)
47-
if tpws ne tpw then tpws else tp
48-
49-
end StripNullsMap
50-
5112
extension (self: Type)
5213
/** Syntactically strips the nullability from this type.
5314
* If the type is `T1 | ... | Tn`, and `Ti` references to `Null`,
@@ -56,22 +17,38 @@ object NullOpsDecorator:
5617
* The type will not be changed if explicit-nulls is not enabled.
5718
*/
5819
def stripNull(using Context): Type = {
59-
if ctx.explicitNulls then new StripNullsMap(false)(self) else self
20+
def strip(tp: Type): Type =
21+
val tpWiden = tp.widenDealias
22+
val tpStripped = tpWiden match {
23+
case tp @ OrType(lhs, rhs) =>
24+
val llhs = strip(lhs)
25+
val rrhs = strip(rhs)
26+
if rrhs.isNullType then llhs
27+
else if llhs.isNullType then rrhs
28+
else tp.derivedOrType(llhs, rrhs)
29+
case tp @ AndType(tp1, tp2) =>
30+
// We cannot `tp.derivedAndType(strip(tp1), strip(tp2))` directly,
31+
// since `stripNull((A | Null) & B)` would produce the wrong
32+
// result `(A & B) | Null`.
33+
val tp1s = strip(tp1)
34+
val tp2s = strip(tp2)
35+
if (tp1s ne tp1) && (tp2s ne tp2) then
36+
tp.derivedAndType(tp1s, tp2s)
37+
else tp
38+
case tp @ TypeBounds(lo, hi) =>
39+
tp.derivedTypeBounds(strip(lo), strip(hi))
40+
case tp => tp
41+
}
42+
if tpStripped ne tpWiden then tpStripped else tp
43+
44+
if ctx.explicitNulls then strip(self) else self
6045
}
6146

6247
/** Is self (after widening and dealiasing) a type of the form `T | Null`? */
6348
def isNullableUnion(using Context): Boolean = {
6449
val stripped = self.stripNull
6550
stripped ne self
6651
}
67-
68-
/** Strips nulls from this type deeply.
69-
* Compaired to `stripNull`, `stripNullsDeep` will apply `stripNull` to
70-
* each member of function types as well.
71-
*/
72-
def stripNullsDeep(using Context): Type =
73-
if ctx.explicitNulls then new StripNullsMap(true)(self) else self
74-
7552
end extension
7653

7754
import ast.tpd._

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

+14-7
Original file line numberDiff line numberDiff line change
@@ -769,13 +769,20 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
769769

770770
isSubType(hi1, tp2, approx.addLow) || compareGADT || tryLiftedToThis1
771771
case _ =>
772-
def isNullable(tp: Type): Boolean = tp.widenDealias match {
773-
case tp: TypeRef => tp.symbol.isNullableClass
774-
case tp: RefinedOrRecType => isNullable(tp.parent)
775-
case tp: AppliedType => isNullable(tp.tycon)
776-
case AndType(tp1, tp2) => isNullable(tp1) && isNullable(tp2)
777-
case OrType(tp1, tp2) => isNullable(tp1) || isNullable(tp2)
778-
case _ => false
772+
// `Mode.RelaxedOverriding` is only enabled when checking Java overriding
773+
// in explicit nulls, and `Null` becomes a bottom type, which allows
774+
// `T | Null` being a subtype of `T`.
775+
// A type varibale `T` from Java is translated to `T >: Nothing <: Any`.
776+
// However, `null` can always be a value of `T` for Java side.
777+
// So the best solution here is to let `Null` be bottom type temporarily.
778+
def isNullable(tp: Type): Boolean = ctx.mode.is(Mode.RelaxedOverriding) || {
779+
tp.widenDealias match
780+
case tp: TypeRef => tp.symbol.isNullableClass
781+
case tp: RefinedOrRecType => isNullable(tp.parent)
782+
case tp: AppliedType => isNullable(tp.tycon)
783+
case AndType(tp1, tp2) => isNullable(tp1) && isNullable(tp2)
784+
case OrType(tp1, tp2) => isNullable(tp1) || isNullable(tp2)
785+
case _ => false
779786
}
780787
val sym1 = tp1.symbol
781788
(sym1 eq NothingClass) && tp2.isValueTypeOrLambda ||

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -1115,10 +1115,10 @@ object Types {
11151115
*/
11161116
def matches(that: Type)(using Context): Boolean = {
11171117
record("matches")
1118-
val thisTp1 = this.stripNullsDeep
1119-
val thatTp1 = that.stripNullsDeep
1120-
withoutMode(Mode.SafeNulls)(
1121-
TypeComparer.matchesType(thisTp1, thatTp1, relaxed = !ctx.phase.erasedTypes))
1118+
val overrideCtx = if ctx.explicitNulls
1119+
then ctx.retractMode(Mode.SafeNulls).addMode(Mode.RelaxedOverriding)
1120+
else ctx
1121+
TypeComparer.matchesType(this, that, relaxed = !ctx.phase.erasedTypes)(using overrideCtx)
11221122
}
11231123

11241124
/** This is the same as `matches` except that it also matches => T with T and

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

+1-6
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,6 @@ class PlainPrinter(_ctx: Context) extends Printer {
5050
tp match {
5151
case tp: ThisType if tp.cls.is(Package) && !tp.cls.isEffectiveRoot =>
5252
requiredPackage(tp.cls.fullName).termRef
53-
case tp: TypeRef =>
54-
val tp1 = tp.dealiasKeepAnnots
55-
if tp1 ne tp then homogenize(tp1) else tp
5653
case tp: TypeVar if tp.isInstantiated =>
5754
homogenize(tp.instanceOpt)
5855
case AndType(tp1, tp2) =>
@@ -63,9 +60,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
6360
if !ctx.mode.is(Mode.Type) && annot.symbol == defn.UncheckedVarianceAnnot =>
6461
homogenize(parent)
6562
case tp: SkolemType =>
66-
// println(tp.info)
67-
// println(tp.info.widen)
68-
homogenize(tp.info)
63+
homogenize(tp.info.dealiasKeepAnnots)
6964
case tp: LazyRef =>
7065
homogenize(tp.ref)
7166
case tp @ AppliedType(tycon, args) =>

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

+8-15
Original file line numberDiff line numberDiff line change
@@ -218,20 +218,13 @@ object OverridingPairs:
218218
}
219219
)
220220
else
221-
def matchNullaryLoosely = member.matchNullaryLoosely || other.matchNullaryLoosely || fallBack
222-
// default getters are not checked for compatibility
223-
member.name.is(DefaultGetterName) || {
224-
if ctx.explicitNulls && (member.is(JavaDefined) || other.is(JavaDefined)) then
225-
// releaxed override check for explicit nulls if one of the symbols is Java defined,
226-
// force `Null` being a subtype of reference types during override checking.
227-
// `stripNullsDeep` is used here because we may encounter type parameters
228-
// (`T | Null` is not a subtype of `T` even if we retract Mode.SafeNulls).
229-
val memberTp1 = memberTp.stripNullsDeep
230-
val otherTp1 = otherTp.stripNullsDeep
231-
withoutMode(Mode.SafeNulls)(
232-
memberTp1.overrides(otherTp1, matchNullaryLoosely))
233-
else
234-
memberTp.overrides(otherTp, matchNullaryLoosely)
235-
}
221+
// releaxed override check for explicit nulls if one of the symbols is Java defined,
222+
// force `Null` being a bottom types during override checking.
223+
val overrideCtx = if ctx.explicitNulls && (member.is(JavaDefined) || other.is(JavaDefined))
224+
then ctx.retractMode(Mode.SafeNulls).addMode(Mode.RelaxedOverriding) else ctx
225+
member.name.is(DefaultGetterName) // default getters are not checked for compatibility
226+
|| memberTp.overrides(otherTp,
227+
member.matchNullaryLoosely || other.matchNullaryLoosely || fallBack
228+
)(using overrideCtx)
236229

237230
end OverridingPairs

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

+4-9
Original file line numberDiff line numberDiff line change
@@ -115,16 +115,11 @@ object ResolveSuper {
115115
val accTp = acc.asSeenFrom(base.typeRef).info
116116
// Since the super class can be Java defined,
117117
// we use releaxed overriding check for explicit nulls if one of the symbols is Java defined.
118-
// This forces `Null` being a subtype of reference types during override checking.
119-
val overridesSuper = if ctx.explicitNulls && (sym.is(JavaDefined) || acc.is(JavaDefined)) then
120-
val otherTp1 = otherTp.stripNullsDeep
121-
val accTp1 = accTp.stripNullsDeep
122-
withoutMode(Mode.SafeNulls)(otherTp1.overrides(accTp1, matchLoosely = true))
123-
else
124-
otherTp.overrides(accTp, matchLoosely = true)
125-
if !overridesSuper then
118+
// This forces `Null` being a bottom type during override checking.
119+
val overrideCtx = if ctx.explicitNulls && (sym.is(JavaDefined) || acc.is(JavaDefined))
120+
then ctx.retractMode(Mode.SafeNulls).addMode(Mode.RelaxedOverriding) else ctx
121+
if !otherTp.overrides(accTp, matchLoosely = true)(using overrideCtx) then
126122
report.error(IllegalSuperAccessor(base, memberName, targetName, acc, accTp, other.symbol, otherTp), base.srcPos)
127-
128123
bcs = bcs.tail
129124
}
130125
assert(sym.exists, i"cannot rebind $acc, ${acc.targetName} $memberName")

0 commit comments

Comments
 (0)