Skip to content

Commit aeafb05

Browse files
committed
Work on rechecker
- comments - some refactorings - enable rechecking tests
1 parent 9ecfbc1 commit aeafb05

File tree

10 files changed

+114
-26
lines changed

10 files changed

+114
-26
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ class Compiler {
7979
new SpecializeApplyMethods, // Adds specialized methods to FunctionN
8080
new TryCatchPatterns, // Compile cases in try/catch
8181
new PatternMatcher) :: // Compile pattern matches
82+
List(new TestRecheck.Pre) :: // Test only: run rechecker, enabled under -Yrecheck-test
83+
List(new TestRecheck) :: // Test only: run rechecker, enabled under -Yrecheck-test
8284
List(new CheckCaptures.Pre) :: // Preparations for check captures phase, enabled under -Ycc
8385
List(new CheckCaptures) :: // Check captures, enabled under -Ycc
8486
List(new ElimOpaque, // Turn opaque into normal aliases

compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ class CheckCaptures extends Recheck, SymTransformer:
128128
class CaptureChecker(ictx: Context) extends Rechecker(ictx):
129129
import ast.tpd.*
130130

131+
override def keepType(tree: Tree) =
132+
super.keepType(tree)
133+
|| tree.isInstanceOf[Try] // type of `try` needs tp be checked for * escapes
134+
131135
private def interpolator(startingVariance: Int = 1)(using Context) = new TypeTraverser:
132136
variance = startingVariance
133137
override def traverse(t: Type) =

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ private sealed trait YSettings:
325325
val YexplicitNulls: Setting[Boolean] = BooleanSetting("-Yexplicit-nulls", "Make reference types non-nullable. Nullable types can be expressed with unions: e.g. String|Null.")
326326
val YcheckInit: Setting[Boolean] = BooleanSetting("-Ysafe-init", "Ensure safe initialization of objects")
327327
val YrequireTargetName: Setting[Boolean] = BooleanSetting("-Yrequire-targetName", "Warn if an operator is defined without a @targetName annotation")
328-
val Yrecheck: Setting[Boolean] = BooleanSetting("-Yrecheck", "Run type rechecks (test only)")
328+
val YrecheckTest: Setting[Boolean] = BooleanSetting("-Yrecheck-test", "Run basic rechecking (internal test only)")
329329
val Ycc: Setting[Boolean] = BooleanSetting("-Ycc", "Check captured references")
330330
val YccDebug: Setting[Boolean] = BooleanSetting("-Ycc-debug", "Debug info for captured references")
331331
val YccNoAbbrev: Setting[Boolean] = BooleanSetting("-Ycc-no-abbrev", "Used in conjunction with -Ycc, suppress type abbreviations")

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import core.Phases.Phase
55
import core.DenotTransformers.DenotTransformer
66
import core.Contexts.{Context, ctx}
77

8-
/** A phase that precedes the rechecker and that allows installing
8+
/** A base class for a phase that precedes a rechecker and that allows installing
99
* new types for local symbols.
1010
*/
1111
abstract class PreRecheck extends Phase, DenotTransformer:

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

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,19 @@ import config.Printers.recheckr
2020
import util.Property
2121
import StdNames.nme
2222
import reporting.trace
23+
import annotation.constructorOnly
2324

2425
object Recheck:
26+
import tpd.Tree
2527

26-
/** A flag used to indicate that a ParamAccessor has been temporily made not-private
28+
/** A flag used to indicate that a ParamAccessor has been temporarily made not-private
2729
* Only used at the start of the Recheck phase, reset at its end.
2830
* The flag repurposes the Scala2ModuleVar flag. No confusion is possible since
2931
* Scala2ModuleVar cannot be also ParamAccessors.
3032
*/
3133
val ResetPrivate = Scala2ModuleVar
3234
val ResetPrivateParamAccessor = ResetPrivate | ParamAccessor
3335

34-
import tpd.Tree
35-
3636
/** Attachment key for rechecked types of TypeTrees */
3737
val RecheckedType = Property.Key[Type]
3838

@@ -83,6 +83,10 @@ object Recheck:
8383

8484
def hasRememberedType: Boolean = tree.hasAttachment(RecheckedType)
8585

86+
/** A base class that runs a simplified typer pass over an already re-typed program. The pass
87+
* does not transform trees but returns instead the re-typed type of each tree as it is
88+
* traversed. The Recheck phase must be directly preceded by a phase of type PreRecheck.
89+
*/
8690
abstract class Recheck extends Phase, SymTransformer:
8791
thisPhase =>
8892

@@ -91,7 +95,6 @@ abstract class Recheck extends Phase, SymTransformer:
9195

9296
def preRecheckPhase = this.prev.asInstanceOf[PreRecheck]
9397

94-
override def isEnabled(using Context) = ctx.settings.Yrecheck.value
9598
override def changesBaseTypes: Boolean = true
9699

97100
override def isCheckable = false
@@ -109,25 +112,36 @@ abstract class Recheck extends Phase, SymTransformer:
109112

110113
def newRechecker()(using Context): Rechecker
111114

112-
class Rechecker(ictx: Context):
115+
/** The typechecker pass */
116+
class Rechecker(@constructorOnly ictx: Context):
113117
private val ta = ictx.typeAssigner
114-
private val keepTypes = inContext(ictx) {
118+
119+
/** If true, remember types of all tree nodes in attachments so that they
120+
* can be retrieved with `knownType`
121+
*/
122+
private val keepAllTypes = inContext(ictx) {
115123
ictx.settings.Xprint.value.containsPhase(thisPhase)
116124
}
117125

118-
def constFold(tree: Tree, tp: Type)(using Context): Type =
126+
/** Should type of `tree` be kept in an attachment so that it can be retrieved with
127+
* `knownType`? By default true only is `keepAllTypes` hold, but can be overridden.
128+
*/
129+
def keepType(tree: Tree): Boolean = keepAllTypes
130+
131+
/** Constant-folded rechecked type `tp` of tree `tree` */
132+
private def constFold(tree: Tree, tp: Type)(using Context): Type =
119133
val tree1 = tree.withType(tp)
120134
val tree2 = ConstFold(tree1)
121135
if tree2 ne tree1 then tree2.tpe else tp
122136

123137
def recheckIdent(tree: Ident)(using Context): Type =
124138
tree.tpe
125139

126-
/** Keep the symbol of the `select` but re-infer its type */
127140
def recheckSelect(tree: Select)(using Context): Type =
128141
val Select(qual, name) = tree
129142
recheckSelection(tree, recheck(qual).widenIfUnstable, name)
130143

144+
/** Keep the symbol of the `select` but re-infer its type */
131145
def recheckSelection(tree: Select, qualType: Type, name: Name)(using Context) =
132146
if name.is(OuterSelectName) then tree.tpe
133147
else
@@ -169,16 +183,17 @@ abstract class Recheck extends Phase, SymTransformer:
169183
recheckStats(impl.body)
170184
sym.typeRef
171185

172-
// Need to remap Object to FromJavaObject since it got lost in ElimRepeated
186+
/** Assuming `formals` are parameters of a Java-defined method, remap Object
187+
* to FromJavaObject since it got lost in ElimRepeated
188+
*/
173189
private def mapJavaArgs(formals: List[Type])(using Context): List[Type] =
174190
val tm = new TypeMap:
175191
def apply(t: Type) = t match
176192
case t: TypeRef if t.symbol == defn.ObjectClass => defn.FromJavaObjectType
177193
case _ => mapOver(t)
178194
formals.mapConserve(tm)
179195

180-
/** Hook for method type instantiation
181-
*/
196+
/** Hook for method type instantiation */
182197
protected def instantiate(mt: MethodType, argTypes: List[Type], sym: Symbol)(using Context): Type =
183198
mt.instantiate(argTypes)
184199

@@ -256,13 +271,21 @@ abstract class Recheck extends Phase, SymTransformer:
256271
recheck(tree.body, pt)
257272

258273
def recheckReturn(tree: Return)(using Context): Type =
259-
val rawType = recheck(tree.expr)
274+
// Avoid local pattern defined symbols in returns from matchResult blocks
275+
// that are inserted by the PatternMatcher transform.
276+
// For regular symbols in the case branch, we already avoid them by the rule
277+
// for blocks since a case branch is of the form `return[MatchResultN] { ... }`
278+
// For source-level returns from methods, there's nothing to avoid, since the
279+
// result type of a method with a return must be given explicitly.
260280
def avoidMap = new TypeOps.AvoidMap:
261281
def toAvoid(tp: NamedType) =
262-
tp.symbol.is(Case) && tp.symbol.owner.isContainedIn(ctx.owner)
282+
tp.symbol.is(Case) && tp.symbol.owner.isContainedIn(ctx.owner)
283+
284+
val rawType = recheck(tree.expr)
263285
val ownType = avoidMap(rawType)
264286
checkConforms(ownType, tree.from.symbol.returnProto, tree)
265287
defn.NothingType
288+
end recheckReturn
266289

267290
def recheckWhileDo(tree: WhileDo)(using Context): Type =
268291
recheck(tree.cond, defn.BooleanType)
@@ -285,7 +308,7 @@ abstract class Recheck extends Phase, SymTransformer:
285308
seqLitType(tree, TypeComparer.lub(declaredElemType :: elemTypes))
286309

287310
def recheckTypeTree(tree: TypeTree)(using Context): Type =
288-
knownType(tree)
311+
knownType(tree) // allows to install new types at Setup
289312

290313
def recheckAnnotated(tree: Annotated)(using Context): Type =
291314
tree.tpe match
@@ -326,10 +349,13 @@ abstract class Recheck extends Phase, SymTransformer:
326349
case tree: ValOrDefDef =>
327350
if tree.isEmpty then NoType
328351
else
329-
if sym.isUpdatedAfter(preRecheckPhase) then sym.ensureCompleted()
330-
else recheckDef(tree, sym)
352+
if sym.isUpdatedAfter(preRecheckPhase) then
353+
sym.ensureCompleted() // in this case the symbol's completer should recheck the right hand side
354+
else
355+
recheckDef(tree, sym)
331356
sym.termRef
332357
case tree: TypeDef =>
358+
// TODO: Should we allow for completers as for ValDefs or DefDefs?
333359
tree.rhs match
334360
case impl: Template =>
335361
recheckClassDef(tree, impl, sym.asClass)(using ctx.localContext(tree, sym))
@@ -364,11 +390,15 @@ abstract class Recheck extends Phase, SymTransformer:
364390
case tree => recheckUnnamed(tree, pt)
365391
end recheckStart
366392

393+
/** Finish rechecking a tree node: check rechecked type against expected type
394+
* and remember rechecked type in a tree attachment if required.
395+
* @param tpe the recheched type of `tree`
396+
* @param tree the rechecked tree
397+
* @param pt the expected type
398+
*/
367399
def recheckFinish(tpe: Type, tree: Tree, pt: Type)(using Context): Type =
368400
checkConforms(tpe, pt, tree)
369-
if keepTypes
370-
|| tree.isInstanceOf[Try] // type needs tp be checked for * escapes
371-
then tree.rememberType(tpe)
401+
if keepType(tree) then tree.rememberType(tpe)
372402
tpe
373403

374404
def recheck(tree: Tree, pt: Type = WildcardType)(using Context): Type =
@@ -379,8 +409,12 @@ abstract class Recheck extends Phase, SymTransformer:
379409
throw ex
380410
}
381411

412+
/** If true, print info for some successful checkConforms operations (failing ones give
413+
* an error message in any case).
414+
*/
382415
private val debugSuccesses = false
383416

417+
/** Check that widened types of `tpe` and `pt` are compatible. */
384418
def checkConforms(tpe: Type, pt: Type, tree: Tree)(using Context): Unit = tree match
385419
case _: DefTree | EmptyTree | _: TypeTree | _: Closure =>
386420
// Don't report closure nodes, since their span is a point; wait instead
@@ -408,6 +442,7 @@ abstract class Recheck extends Phase, SymTransformer:
408442

409443
end Rechecker
410444

445+
/** Show tree with rechecked types instead of the types stored in the `.tpe` field */
411446
override def show(tree: untpd.Tree)(using Context): String =
412447
val addRecheckedTypes = new TreeMap:
413448
override def transform(tree: Tree)(using Context): Tree =
@@ -420,12 +455,14 @@ abstract class Recheck extends Phase, SymTransformer:
420455
}
421456
end Recheck
422457

458+
/** A class that can be used to test basic rechecking without any customaization */
423459
object TestRecheck:
424-
class Pre extends PreRecheck, IdentityDenotTransformer
460+
class Pre extends PreRecheck, IdentityDenotTransformer:
461+
override def isEnabled(using Context) = ctx.settings.YrecheckTest.value
425462

426463
class TestRecheck extends Recheck:
427464
def phaseName: String = "recheck"
428-
//override def isEnabled(using Context) = ctx.settings.YrefineTypes.value
465+
override def isEnabled(using Context) = ctx.settings.YrecheckTest.value
429466
def newRechecker()(using Context): Rechecker = Rechecker(ctx)
430467

431468

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
11
# Cannot compensate dealiasing due to false result dependency
22
i6635a.scala
33
i6682a.scala
4+
5+
# unknown reason
6+
patmatch-in-catch.scala
7+
t0095.scala
8+
i8344-1.scala
9+
i8900-promote.scala
10+
i6199c.scala
11+
gadt-TailCalls.scala
12+
i13349.scala
13+
bynamefuns.scala
14+
i15174.scala
15+
gadt-cast-singleton.scala
16+
6709.scala
17+
11463.scala
18+
i11247.scala
19+
i6199b.scala
20+
t3440.scala
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# unknown reason
2+
i10930.scala
3+
t7584b.scala
4+
opaque-inline
5+
fragables-extension.scala
6+
t7899.scala
7+
t7181.scala
8+
option-extract.scala
9+
i5976.scala
10+
tagless.scala
11+
safeThrowsStrawman2.scala
12+
t7584.scala
13+
function-arity.scala

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,9 @@ class CompilationTests {
241241
given TestGroup = TestGroup("recheck")
242242
aggregateTests(
243243
compileFilesInDir("tests/new", recheckOptions),
244-
//Following are disabled since they take too long for what they provide.
244+
compileFilesInDir("tests/run", recheckOptions, FileFilter.exclude(TestSources.runTestRecheckExcluded))
245+
//Disabled to save some time.
245246
//compileFilesInDir("tests/pos", recheckOptions, FileFilter.exclude(TestSources.posTestRecheckExcluded)),
246-
//compileFilesInDir("tests/run", recheckOptions, FileFilter.exclude(TestSources.runTestRecheckExcluded))
247247
).checkCompile()
248248

249249
// Explicit nulls tests

compiler/test/dotty/tools/vulpix/TestConfiguration.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ object TestConfiguration {
8383
)
8484
val picklingWithCompilerOptions =
8585
picklingOptions.withClasspath(withCompilerClasspath).withRunClasspath(withCompilerClasspath)
86-
val recheckOptions = defaultOptions.and("-Yrecheck")
86+
val recheckOptions = defaultOptions.and("-Yrecheck-test")
8787
val scala2CompatMode = defaultOptions.and("-source", "3.0-migration")
8888
val explicitUTF8 = defaultOptions and ("-encoding", "UTF8")
8989
val explicitUTF16 = defaultOptions and ("-encoding", "UTF16")

tests/pos/patmatch-in-catch.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
sealed abstract class Action
2+
case object MainReturn extends Action
3+
case object ExceptionNormalExit extends Action
4+
case class CaughtException(action: Action) extends RuntimeException
5+
6+
def driver(action: Action): Action =
7+
val result =
8+
try action
9+
catch case CaughtException(action) =>
10+
action match
11+
case ExceptionNormalExit =>
12+
action
13+
case _ =>
14+
???
15+
result

0 commit comments

Comments
 (0)