Skip to content

Commit 3002842

Browse files
committed
Changes to compiler codebase so that it passes capture checking
# Conflicts: # tests/pos-with-compiler-cc/dotc/ast/Trees.scala
1 parent 6c08fce commit 3002842

File tree

93 files changed

+534
-1092
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+534
-1092
lines changed

tests/pos-with-compiler-cc/dotc/Run.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import java.nio.charset.StandardCharsets
3131
import scala.collection.mutable
3232
import scala.util.control.NonFatal
3333
import scala.io.Codec
34+
import caps.unsafe.unsafeUnbox
3435

3536
/** A compiler run. Exports various methods to compile source files */
3637
class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with ConstraintRunInfo {
@@ -270,7 +271,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
270271
Rewrites.writeBack()
271272
suppressions.runFinished(hasErrors = ctx.reporter.hasErrors)
272273
while (finalizeActions.nonEmpty) {
273-
val action = finalizeActions.remove(0)
274+
val action = finalizeActions.remove(0).unsafeUnbox
274275
action()
275276
}
276277
compiling = false

tests/pos-with-compiler-cc/dotc/ast/Desugar.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,7 @@ object desugar {
673673
else (Nil, Nil)
674674
}
675675

676-
var parents1 = parents
676+
var parents1: List[untpd.Tree] = parents // !cc! need explicit type to make capture checking pass
677677
if (isEnumCase && parents.isEmpty)
678678
parents1 = enumClassTypeRef :: Nil
679679
if (isNonEnumCase)
@@ -1779,7 +1779,10 @@ object desugar {
17791779
val elems = segments flatMap {
17801780
case ts: Thicket => ts.trees.tail
17811781
case t => Nil
1782-
} map {
1782+
} map { (t: Tree) => t match
1783+
// !cc! explicitly typed parameter (t: Tree) is needed since otherwise
1784+
// we get an error similar to #16268. (The explicit type constrains the type of `segments`
1785+
// which is otherwise List[{*} tree])
17831786
case Block(Nil, EmptyTree) => Literal(Constant(())) // for s"... ${} ..."
17841787
case Block(Nil, expr) => expr // important for interpolated string as patterns, see i1773.scala
17851788
case t => t

tests/pos-with-compiler-cc/dotc/ast/Positioned.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import annotation.internal.sharable
1515

1616
/** A base class for things that have positions (currently: modifiers and trees)
1717
*/
18-
abstract class Positioned(implicit @constructorOnly src: SourceFile) extends SrcPos, Product, Cloneable {
18+
abstract class Positioned(implicit @constructorOnly src: SourceFile) extends SrcPos, Product, Cloneable, caps.Pure {
1919
import Positioned.{ids, nextId, debugId}
2020

2121
private var mySpan: Span = _

tests/pos-with-compiler-cc/dotc/ast/TreeTypeMap.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Types._, Contexts._, Flags._
77
import Symbols._, Annotations._, Trees._, Symbols._, Constants.Constant
88
import Decorators._
99
import dotty.tools.dotc.transform.SymUtils._
10+
import language.experimental.pureFunctions
1011

1112
/** A map that applies three functions and a substitution together to a tree and
1213
* makes sure they are coordinated so that the result is well-typed. The functions are
@@ -32,8 +33,8 @@ import dotty.tools.dotc.transform.SymUtils._
3233
* set, we would get a data race assertion error.
3334
*/
3435
class TreeTypeMap(
35-
val typeMap: Type => Type = IdentityTypeMap,
36-
val treeMap: tpd.Tree => tpd.Tree = identity _,
36+
val typeMap: Type -> Type = IdentityTypeMap,
37+
val treeMap: tpd.Tree -> tpd.Tree = identity[tpd.Tree](_), // !cc! need explicit instantiation of default argument
3738
val oldOwners: List[Symbol] = Nil,
3839
val newOwners: List[Symbol] = Nil,
3940
val substFrom: List[Symbol] = Nil,
@@ -42,8 +43,8 @@ class TreeTypeMap(
4243
import tpd._
4344

4445
def copy(
45-
typeMap: Type => Type,
46-
treeMap: tpd.Tree => tpd.Tree,
46+
typeMap: Type -> Type,
47+
treeMap: tpd.Tree -> tpd.Tree,
4748
oldOwners: List[Symbol],
4849
newOwners: List[Symbol],
4950
substFrom: List[Symbol],

tests/pos-with-compiler-cc/dotc/ast/Trees.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import annotation.unchecked.uncheckedVariance
1717
import annotation.constructorOnly
1818
import compiletime.uninitialized
1919
import Decorators._
20+
import annotation.retains
21+
import language.experimental.pureFunctions
2022

2123
object Trees {
2224

@@ -47,7 +49,7 @@ object Trees {
4749
* nodes.
4850
*/
4951
abstract class Tree[+T <: Untyped](implicit @constructorOnly src: SourceFile)
50-
extends Positioned, SrcPos, Product, Attachment.Container, printing.Showable {
52+
extends Positioned, SrcPos, Product, Attachment.Container, printing.Showable, caps.Pure {
5153

5254
if (Stats.enabled) ntrees += 1
5355

@@ -431,7 +433,7 @@ object Trees {
431433
def isBackquoted: Boolean = hasAttachment(Backquoted)
432434
}
433435

434-
class SearchFailureIdent[+T <: Untyped] private[ast] (name: Name, expl: => String)(implicit @constructorOnly src: SourceFile)
436+
class SearchFailureIdent[+T <: Untyped] private[ast] (name: Name, expl: -> String)(implicit @constructorOnly src: SourceFile)
435437
extends Ident[T](name) {
436438
def explanation = expl
437439
override def toString: String = s"SearchFailureIdent($explanation)"
@@ -1518,7 +1520,7 @@ object Trees {
15181520
}
15191521
}
15201522

1521-
abstract class TreeAccumulator[X] { self =>
1523+
abstract class TreeAccumulator[X] { self: TreeAccumulator[X] @retains(caps.*) =>
15221524
// Ties the knot of the traversal: call `foldOver(x, tree))` to dive in the `tree` node.
15231525
def apply(x: X, tree: Tree)(using Context): X
15241526

tests/pos-with-compiler-cc/dotc/ast/tpd.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import typer.ConstFold
1818

1919
import scala.annotation.tailrec
2020
import scala.collection.mutable.ListBuffer
21+
import language.experimental.pureFunctions
2122

2223
/** Some creators for typed trees */
2324
object tpd extends Trees.Instance[Type] with TypedTreeInfo {
@@ -1454,7 +1455,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
14541455
* @return The symbols imported.
14551456
*/
14561457
def importedSymbols(imp: Import,
1457-
selectorPredicate: untpd.ImportSelector => Boolean = util.common.alwaysTrue)
1458+
selectorPredicate: untpd.ImportSelector -> Boolean = util.common.alwaysTrue)
14581459
(using Context): List[Symbol] =
14591460
imp.selectors.find(selectorPredicate) match
14601461
case Some(sel) => importedSymbols(imp.expr, sel.name)

tests/pos-with-compiler-cc/dotc/ast/untpd.scala

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import util.Spans.Span
1111
import annotation.constructorOnly
1212
import annotation.internal.sharable
1313
import Decorators._
14+
import annotation.retains
15+
import language.experimental.pureFunctions
1416

1517
object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
1618

@@ -149,7 +151,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
149151
case class CapturingTypeTree(refs: List[Tree], parent: Tree)(implicit @constructorOnly src: SourceFile) extends TypTree
150152

151153
/** Short-lived usage in typer, does not need copy/transform/fold infrastructure */
152-
case class DependentTypeTree(tp: List[Symbol] => Type)(implicit @constructorOnly src: SourceFile) extends Tree
154+
case class DependentTypeTree(tp: List[Symbol] -> Type)(implicit @constructorOnly src: SourceFile) extends Tree
153155

154156
@sharable object EmptyTypeIdent extends Ident(tpnme.EMPTY)(NoSource) with WithoutTypeOrPos[Untyped] {
155157
override def isEmpty: Boolean = true
@@ -369,7 +371,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
369371
// ------ Creation methods for untyped only -----------------
370372

371373
def Ident(name: Name)(implicit src: SourceFile): Ident = new Ident(name)
372-
def SearchFailureIdent(name: Name, explanation: => String)(implicit src: SourceFile): SearchFailureIdent = new SearchFailureIdent(name, explanation)
374+
def SearchFailureIdent(name: Name, explanation: -> String)(implicit src: SourceFile): SearchFailureIdent = new SearchFailureIdent(name, explanation)
373375
def Select(qualifier: Tree, name: Name)(implicit src: SourceFile): Select = new Select(qualifier, name)
374376
def SelectWithSig(qualifier: Tree, name: Name, sig: Signature)(implicit src: SourceFile): Select = new SelectWithSig(qualifier, name, sig)
375377
def This(qual: Ident)(implicit src: SourceFile): This = new This(qual)
@@ -731,7 +733,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
731733
}
732734
}
733735

734-
abstract class UntypedTreeAccumulator[X] extends TreeAccumulator[X] { self =>
736+
abstract class UntypedTreeAccumulator[X] extends TreeAccumulator[X] {
737+
self: UntypedTreeAccumulator[X] @retains(caps.*) =>
735738
override def foldMoreCases(x: X, tree: Tree)(using Context): X = tree match {
736739
case ModuleDef(name, impl) =>
737740
this(x, impl)

tests/pos-with-compiler-cc/dotc/cc/CaptureSet.scala

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import util.{SimpleIdentitySet, Property}
1616
import util.common.alwaysTrue
1717
import scala.collection.mutable
1818
import config.Config.ccAllowUnsoundMaps
19+
import language.experimental.pureFunctions
1920

2021
/** A class for capture sets. Capture sets can be constants or variables.
2122
* Capture sets support inclusion constraints <:< where <:< is subcapturing.
@@ -37,7 +38,7 @@ import config.Config.ccAllowUnsoundMaps
3738
* if the mapped function is either a bijection or if it is idempotent
3839
* on capture references (c.f. doc comment on `map` below).
3940
*/
40-
sealed abstract class CaptureSet extends Showable:
41+
sealed abstract class CaptureSet extends Showable, caps.Pure:
4142
import CaptureSet.*
4243

4344
/** The elements of this capture set. For capture variables,
@@ -222,7 +223,7 @@ sealed abstract class CaptureSet extends Showable:
222223
/** The largest subset (via <:<) of this capture set that only contains elements
223224
* for which `p` is true.
224225
*/
225-
def filter(p: CaptureRef => Boolean)(using Context): CaptureSet =
226+
def filter(p: CaptureRef -> Boolean)(using Context): CaptureSet =
226227
if this.isConst then
227228
val elems1 = elems.filter(p)
228229
if elems1 == elems then this
@@ -372,8 +373,10 @@ object CaptureSet:
372373
def isConst = isSolved
373374
def isAlwaysEmpty = false
374375

375-
/** A handler to be invoked if the root reference `*` is added to this set */
376-
var rootAddedHandler: () => Context ?=> Unit = () => ()
376+
/** A handler to be invoked if the root reference `*` is added to this set
377+
* The handler is pure in the sense that it will only output diagnostics.
378+
*/
379+
var rootAddedHandler: () -> Context ?-> Unit = () => ()
377380

378381
var description: String = ""
379382

@@ -421,7 +424,7 @@ object CaptureSet:
421424
else
422425
CompareResult.fail(this)
423426

424-
override def disallowRootCapability(handler: () => Context ?=> Unit)(using Context): this.type =
427+
override def disallowRootCapability(handler: () -> Context ?-> Unit)(using Context): this.type =
425428
rootAddedHandler = handler
426429
super.disallowRootCapability(handler)
427430

@@ -613,7 +616,7 @@ object CaptureSet:
613616

614617
/** A variable with elements given at any time as { x <- source.elems | p(x) } */
615618
class Filtered private[CaptureSet]
616-
(val source: Var, p: CaptureRef => Boolean)(using @constructorOnly ctx: Context)
619+
(val source: Var, p: CaptureRef -> Boolean)(using @constructorOnly ctx: Context)
617620
extends DerivedVar(source.elems.filter(p)):
618621

619622
override def addNewElems(newElems: Refs, origin: CaptureSet)(using Context, VarState): CompareResult =

tests/pos-with-compiler-cc/dotc/cc/CheckCaptures.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import CaptureSet.{withCaptureSetsExplained, IdempotentCaptRefMap}
2121
import StdNames.nme
2222
import NameKinds.DefaultGetterName
2323
import reporting.trace
24+
import language.experimental.pureFunctions
2425

2526
/** The capture checker */
2627
object CheckCaptures:
@@ -721,20 +722,21 @@ class CheckCaptures extends Recheck, SymTransformer:
721722
* the innermost capturing type. The outer capture annotations can be
722723
* reconstructed with the returned function.
723724
*/
724-
def destructCapturingType(tp: Type, reconstruct: Type => Type = x => x): ((Type, CaptureSet, Boolean), Type => Type) =
725+
def destructCapturingType(tp: Type, reconstruct: Type -> Type = (x: Type) => x) // !cc! need monomorphic default argument
726+
: (Type, CaptureSet, Boolean, Type -> Type) =
725727
tp.dealias match
726728
case tp @ CapturingType(parent, cs) =>
727729
if parent.dealias.isCapturingType then
728730
destructCapturingType(parent, res => reconstruct(tp.derivedCapturingType(res, cs)))
729731
else
730-
((parent, cs, tp.isBoxed), reconstruct)
732+
(parent, cs, tp.isBoxed, reconstruct)
731733
case actual =>
732-
((actual, CaptureSet(), false), reconstruct)
734+
(actual, CaptureSet(), false, reconstruct)
733735

734736
def adapt(actual: Type, expected: Type, covariant: Boolean): Type = trace(adaptInfo(actual, expected, covariant), recheckr, show = true) {
735737
if expected.isInstanceOf[WildcardType] then actual
736738
else
737-
val ((parent, cs, actualIsBoxed), recon) = destructCapturingType(actual)
739+
val (parent, cs, actualIsBoxed, recon: (Type -> Type)) = destructCapturingType(actual)
738740

739741
val needsAdaptation = actualIsBoxed != expected.isBoxedCapturing
740742
val insertBox = needsAdaptation && covariant != actualIsBoxed

tests/pos-with-compiler-cc/dotc/classpath/DirectoryClassPath.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import PlainFile.toPlainFile
1717
import scala.jdk.CollectionConverters._
1818
import scala.collection.immutable.ArraySeq
1919
import scala.util.control.NonFatal
20+
import language.experimental.pureFunctions
2021

2122
/**
2223
* A trait allowing to look for classpath entries in directories. It provides common logic for
@@ -32,7 +33,7 @@ trait DirectoryLookup[FileEntryType <: ClassRepresentation] extends EfficientCla
3233

3334
protected def emptyFiles: Array[F] // avoids reifying ClassTag[F]
3435
protected def getSubDir(dirName: String): Option[F]
35-
protected def listChildren(dir: F, filter: Option[F => Boolean] = None): Array[F]
36+
protected def listChildren(dir: F, filter: Option[F -> Boolean] = (None: Option[F -> Boolean])): Array[F] // !cc! need explicit typing of default argument
3637
protected def getName(f: F): String
3738
protected def toAbstractFile(f: F): AbstractFile
3839
protected def isPackage(f: F): Boolean
@@ -90,7 +91,7 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends Directo
9091
if (packageDir.exists && packageDir.isDirectory) Some(packageDir)
9192
else None
9293
}
93-
protected def listChildren(dir: JFile, filter: Option[JFile => Boolean]): Array[JFile] = {
94+
protected def listChildren(dir: JFile, filter: Option[JFile -> Boolean]): Array[JFile] = {
9495
val listing = filter match {
9596
case Some(f) => dir.listFiles(mkFileFilter(f))
9697
case None => dir.listFiles()

tests/pos-with-compiler-cc/dotc/classpath/FileUtils.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import scala.language.unsafeNulls
99
import java.io.{File => JFile, FileFilter}
1010
import java.net.URL
1111
import dotty.tools.io.AbstractFile
12+
import language.experimental.pureFunctions
1213

1314
/**
1415
* Common methods related to Java files and abstract files used in the context of classpath
@@ -78,7 +79,7 @@ object FileUtils {
7879
def mayBeValidPackage(dirName: String): Boolean =
7980
(dirName != "META-INF") && (dirName != "") && (dirName.charAt(0) != '.')
8081

81-
def mkFileFilter(f: JFile => Boolean): FileFilter = new FileFilter {
82+
def mkFileFilter(f: JFile -> Boolean): FileFilter = new FileFilter {
8283
def accept(pathname: JFile): Boolean = f(pathname)
8384
}
8485
}

tests/pos-with-compiler-cc/dotc/classpath/VirtualDirectoryClassPath.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import FileUtils._
88
import java.net.URL
99

1010
import dotty.tools.io.ClassPath
11+
import language.experimental.pureFunctions
1112

1213
case class VirtualDirectoryClassPath(dir: VirtualDirectory) extends ClassPath with DirectoryLookup[ClassFileEntryImpl] with NoSourcePaths {
1314
type F = AbstractFile
@@ -28,7 +29,7 @@ case class VirtualDirectoryClassPath(dir: VirtualDirectory) extends ClassPath wi
2829
protected def emptyFiles: Array[AbstractFile] = Array.empty
2930
protected def getSubDir(packageDirName: String): Option[AbstractFile] =
3031
Option(lookupPath(dir)(packageDirName.split(java.io.File.separator).toIndexedSeq, directory = true))
31-
protected def listChildren(dir: AbstractFile, filter: Option[AbstractFile => Boolean] = None): Array[F] = filter match {
32+
protected def listChildren(dir: AbstractFile, filter: Option[AbstractFile -> Boolean]): Array[F] = filter match {
3233
case Some(f) => dir.iterator.filter(f).toArray
3334
case _ => dir.toArray
3435
}

tests/pos-with-compiler-cc/dotc/config/Config.scala

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,6 @@ object Config {
2222
*/
2323
inline val checkConstraintsNonCyclic = false
2424

25-
/** Check that reverse dependencies in constraints are correct and complete.
26-
* Can also be enabled using -Ycheck-constraint-deps.
27-
*/
28-
inline val checkConstraintDeps = false
29-
3025
/** Check that each constraint resulting from a subtype test
3126
* is satisfiable. Also check that a type variable instantiation
3227
* satisfies its constraints.
@@ -189,9 +184,6 @@ object Config {
189184
/** If set, prints a trace of all symbol completions */
190185
inline val showCompletions = false
191186

192-
/** If set, show variable/variable reverse dependencies when printing constraints. */
193-
inline val showConstraintDeps = true
194-
195187
/** If set, method results that are context functions are flattened by adding
196188
* the parameters of the context function results to the methods themselves.
197189
* This is an optimization that reduces closure allocations.

tests/pos-with-compiler-cc/dotc/config/Feature.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import util.{SrcPos, NoSourcePosition}
1010
import SourceVersion._
1111
import reporting.Message
1212
import NameKinds.QualifiedName
13+
import language.experimental.pureFunctions
1314

1415
object Feature:
1516

@@ -127,7 +128,7 @@ object Feature:
127128
else
128129
false
129130

130-
def checkExperimentalFeature(which: String, srcPos: SrcPos, note: => String = "")(using Context) =
131+
def checkExperimentalFeature(which: String, srcPos: SrcPos, note: -> String = "")(using Context) =
131132
if !isExperimentalEnabled then
132133
report.error(i"Experimental $which may only be used with a nightly or snapshot version of the compiler$note", srcPos)
133134

tests/pos-with-compiler-cc/dotc/config/ScalaSettings.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class ScalaSettings extends SettingGroup with AllScalaSettings
1717
object ScalaSettings:
1818
// Keep synchronized with `classfileVersion` in `BCodeIdiomatic`
1919
private val minTargetVersion = 8
20-
private val maxTargetVersion = 20
20+
private val maxTargetVersion = 19
2121

2222
def supportedTargetVersions: List[String] =
2323
(minTargetVersion to maxTargetVersion).toList.map(_.toString)
@@ -308,7 +308,6 @@ private sealed trait YSettings:
308308
val YforceSbtPhases: Setting[Boolean] = BooleanSetting("-Yforce-sbt-phases", "Run the phases used by sbt for incremental compilation (ExtractDependencies and ExtractAPI) even if the compiler is ran outside of sbt, for debugging.")
309309
val YdumpSbtInc: Setting[Boolean] = BooleanSetting("-Ydump-sbt-inc", "For every compiled foo.scala, output the API representation and dependencies used for sbt incremental compilation in foo.inc, implies -Yforce-sbt-phases.")
310310
val YcheckAllPatmat: Setting[Boolean] = BooleanSetting("-Ycheck-all-patmat", "Check exhaustivity and redundancy of all pattern matching (used for testing the algorithm).")
311-
val YcheckConstraintDeps: Setting[Boolean] = BooleanSetting("-Ycheck-constraint-deps", "Check dependency tracking in constraints (used for testing the algorithm).")
312311
val YretainTrees: Setting[Boolean] = BooleanSetting("-Yretain-trees", "Retain trees for top-level classes, accessible from ClassSymbol#tree")
313312
val YshowTreeIds: Setting[Boolean] = BooleanSetting("-Yshow-tree-ids", "Uniquely tag all tree nodes in debugging output.")
314313
val YfromTastyIgnoreList: Setting[List[String]] = MultiStringSetting("-Yfrom-tasty-ignore-list", "file", "List of `tasty` files in jar files that will not be loaded when using -from-tasty")
@@ -330,7 +329,6 @@ private sealed trait YSettings:
330329
val YrecheckTest: Setting[Boolean] = BooleanSetting("-Yrecheck-test", "Run basic rechecking (internal test only)")
331330
val YccDebug: Setting[Boolean] = BooleanSetting("-Ycc-debug", "Used in conjunction with captureChecking language import, debug info for captured references")
332331
val YccNoAbbrev: Setting[Boolean] = BooleanSetting("-Ycc-no-abbrev", "Used in conjunction with captureChecking language import, suppress type abbreviations")
333-
val YlightweightLazyVals: Setting[Boolean] = BooleanSetting("-Ylightweight-lazy-vals", "Use experimental lightweight implementation of lazy vals")
334332

335333
/** Area-specific debug output */
336334
val YexplainLowlevel: Setting[Boolean] = BooleanSetting("-Yexplain-lowlevel", "When explaining type errors, show types at a lower level.")

0 commit comments

Comments
 (0)