Skip to content

Commit c7fa2ca

Browse files
committed
Fix checking ctx to carry correct modes
1 parent 004a2fd commit c7fa2ca

File tree

10 files changed

+141
-17
lines changed

10 files changed

+141
-17
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ class Compiler {
101101
new TupleOptimizations, // Optimize generic operations on tuples
102102
new LetOverApply, // Lift blocks from receivers of applications
103103
new ArrayConstructors) :: // Intercept creation of (non-generic) arrays and intrinsify.
104+
List(new DropImports) :: // Drop all (language-) imports
104105
List(new Erasure) :: // Rewrite types to JVM model, erasing all type parameters, abstract types and refinements.
105106
List(new ElimErasedValueType, // Expand erased value types to their underlying implmementation types
106107
new PureStats, // Remove pure stats from blocks
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import ast.tpd
5+
import core.*
6+
import Contexts.*
7+
import MegaPhase.MiniPhase
8+
import Decorators.*
9+
10+
/** This phase finally drops all (language-) imports.
11+
* Since some of the language imports change the subtyping,
12+
* we cannot check the trees before erasure. Therefore, this
13+
* phase should be the last phase before erasure.
14+
*/
15+
object DropImports:
16+
val name: String = "dropImports"
17+
val description: String = "drop all (language-) imports."
18+
19+
class DropImports extends MiniPhase:
20+
import tpd._
21+
22+
override def phaseName: String = DropImports.name
23+
24+
override def description: String = DropImports.description
25+
26+
override def isCheckable: Boolean = false
27+
28+
override def transformOther(tree: Tree)(using Context): Tree = tree match
29+
case tree: Import => EmptyTree
30+
case _ => tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ class Erasure extends Phase with DenotTransformer {
153153
override def checkPostCondition(tree: tpd.Tree)(using Context): Unit = {
154154
assertErased(tree)
155155
tree match {
156+
case _: tpd.Import => assert(false, i"illegal tree: $tree")
156157
case res: tpd.This =>
157158
assert(!ExplicitOuter.referencesOuter(ctx.owner.lexicallyEnclosingClass, res),
158159
i"Reference to $res from ${ctx.owner.showLocated}")

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Symbols._, Types._, Contexts._, DenotTransformers._, Flags._
66
import util.Spans._
77
import SymUtils._
88
import StdNames._, NameOps._
9+
import typer.Nullables
910

1011
class MixinOps(cls: ClassSymbol, thisPhase: DenotTransformer)(using Context) {
1112
import ast.tpd._
@@ -80,13 +81,20 @@ class MixinOps(cls: ClassSymbol, thisPhase: DenotTransformer)(using Context) {
8081
prefss =>
8182
val (targs, vargss) = splitArgs(prefss)
8283
val tapp = superRef(target).appliedToTypeTrees(targs)
83-
vargss match
84+
val rhs = vargss match
8485
case Nil | List(Nil) =>
8586
// Overriding is somewhat loose about `()T` vs `=> T`, so just pick
8687
// whichever makes sense for `target`
8788
tapp.ensureApplied
8889
case _ =>
8990
tapp.appliedToArgss(vargss)
91+
if ctx.explicitNulls && target.is(JavaDefined) && !ctx.phase.erasedTypes then
92+
// We may forward to a super Java member in resolveSuper phase.
93+
// Since this is still before erasure, the type can be nullable
94+
// and causes error during checking. So we need to enable
95+
// unsafe-nulls to construct the rhs.
96+
Block(Nullables.importUnsafeNulls :: Nil, rhs)
97+
else rhs
9098

9199
private def competingMethodsIterator(meth: Symbol): Iterator[Symbol] =
92100
cls.baseClasses.iterator

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

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import Decorators.*
2020
* The phase also replaces all expressions that appear in an erased context by
2121
* default values. This is necessary so that subsequent checking phases such
2222
* as IsInstanceOfChecker don't give false negatives.
23-
* Finally, the phase drops (language-) imports.
2423
*/
2524
class PruneErasedDefs extends MiniPhase with SymTransformer { thisTransform =>
2625
import tpd._
@@ -56,18 +55,10 @@ class PruneErasedDefs extends MiniPhase with SymTransformer { thisTransform =>
5655
checkErasedInExperimental(tree.symbol)
5756
tree
5857

59-
override def transformOther(tree: Tree)(using Context): Tree = tree match
60-
case tree: Import => EmptyTree
61-
case _ => tree
62-
6358
def checkErasedInExperimental(sym: Symbol)(using Context): Unit =
6459
// Make an exception for Scala 2 experimental macros to allow dual Scala 2/3 macros under non experimental mode
6560
if sym.is(Erased, butNot = Macro) && sym != defn.Compiletime_erasedValue && !sym.isInExperimentalScope then
6661
Feature.checkExperimentalFeature("erased", sym.sourcePos)
67-
68-
override def checkPostCondition(tree: Tree)(using Context): Unit = tree match
69-
case _: tpd.Import => assert(false, i"illegal tree: $tree")
70-
case _ =>
7162
}
7263

7364
object PruneErasedDefs {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ class TreeChecker extends Phase with SymTransformer {
134134

135135
val checkingCtx = ctx
136136
.fresh
137-
.setMode(Mode.ImplicitsEnabled)
137+
.addMode(Mode.ImplicitsEnabled)
138138
.setReporter(new ThrowingReporter(ctx.reporter))
139139

140140
val checker = inContext(ctx) {

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ import ast.Trees.mods
2020
object Nullables:
2121
import ast.tpd._
2222

23+
def importUnsafeNulls(using Context): Import = Import(
24+
ref(defn.LanguageModule),
25+
List(untpd.ImportSelector(untpd.Ident(nme.unsafeNulls), EmptyTree, EmptyTree)))
26+
2327
inline def unsafeNullsEnabled(using Context): Boolean =
2428
ctx.explicitNulls && !ctx.mode.is(Mode.SafeNulls)
2529

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1059,7 +1059,18 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
10591059
val (stats1, exprCtx) = withoutMode(Mode.Pattern) {
10601060
typedBlockStats(tree.stats)
10611061
}
1062-
val expr1 = typedExpr(tree.expr, pt.dropIfProto)(using exprCtx)
1062+
var expr1 = typedExpr(tree.expr, pt.dropIfProto)(using exprCtx)
1063+
1064+
// If unsafe nulls is enabled inside a block but not enabled outside
1065+
// and the type does not conform the expected type without unsafe nulls,
1066+
// we will cast the last expression to the expected type.
1067+
// See: tests/explicit-nulls/pos/unsafe-block.scala
1068+
if ctx.mode.is(Mode.SafeNulls)
1069+
&& !exprCtx.mode.is(Mode.SafeNulls)
1070+
&& pt.isValueType
1071+
&& !inContext(exprCtx.addMode(Mode.SafeNulls))(expr1.tpe <:< pt) then
1072+
expr1 = expr1.cast(pt)
1073+
10631074
ensureNoLocalRefs(
10641075
cpy.Block(tree)(stats1, expr1)
10651076
.withType(expr1.tpe)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
def trim(x: String | Null): String =
2+
import scala.language.unsafeNulls
3+
// The type of `x.trim()` is `String | Null`.
4+
// Although `String | Null` conforms the expected type `String`,
5+
// we still need to cast the expression to the expected type here,
6+
// because outside the scope we don't have `unsafeNulls` anymore.
7+
x.trim()
8+
9+
class TestDefs:
10+
11+
def f1: String | Null = null
12+
def f2: Array[String | Null] | Null = null
13+
def f3: Array[String] | Null = null
14+
15+
def h1a: String =
16+
import scala.language.unsafeNulls
17+
f1
18+
19+
def h1b: String | Null =
20+
import scala.language.unsafeNulls
21+
f1
22+
23+
def h2a: Array[String] =
24+
import scala.language.unsafeNulls
25+
f2
26+
27+
def h2b: Array[String | Null] =
28+
import scala.language.unsafeNulls
29+
f2
30+
31+
def h3a: Array[String] =
32+
import scala.language.unsafeNulls
33+
f3
34+
35+
def h3b: Array[String | Null] =
36+
import scala.language.unsafeNulls
37+
f3
38+
39+
class TestVals:
40+
41+
val f1: String | Null = null
42+
val f2: Array[String | Null] | Null = null
43+
val f3: Array[String] | Null = null
44+
45+
val h1a: String =
46+
import scala.language.unsafeNulls
47+
f1
48+
49+
val h1b: String | Null =
50+
import scala.language.unsafeNulls
51+
f1
52+
53+
val h2a: Array[String] =
54+
import scala.language.unsafeNulls
55+
f2
56+
57+
val h2b: Array[String | Null] =
58+
import scala.language.unsafeNulls
59+
f2
60+
61+
val h3a: Array[String] =
62+
import scala.language.unsafeNulls
63+
f3
64+
65+
val h3b: Array[String | Null] =
66+
import scala.language.unsafeNulls
67+
f3
Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
import java.nio.file.FileSystems
22
import java.util.ArrayList
33

4-
def directorySeparator: String =
5-
import scala.language.unsafeNulls
6-
FileSystems.getDefault().getSeparator()
4+
class A:
5+
6+
def directorySeparator: String =
7+
import scala.language.unsafeNulls
8+
FileSystems.getDefault().getSeparator()
9+
10+
def getFirstOfFirst(xs: ArrayList[ArrayList[ArrayList[String]]]): String =
11+
import scala.language.unsafeNulls
12+
xs.get(0).get(0).get(0)
713

8-
def getFirstOfFirst(xs: ArrayList[ArrayList[ArrayList[String]]]): String =
14+
class B:
915
import scala.language.unsafeNulls
10-
xs.get(0).get(0).get(0)
16+
17+
def directorySeparator: String =
18+
FileSystems.getDefault().getSeparator()
19+
20+
def getFirstOfFirst(xs: ArrayList[ArrayList[ArrayList[String]]]): String =
21+
xs.get(0).get(0).get(0)

0 commit comments

Comments
 (0)