Skip to content

Commit 76b1805

Browse files
committed
Merge branch 'emit-deferred-reachability-warnings' into check-ordinal-match
* emit-deferred-reachability-warnings: Restore adaptType, with expanded its docs & tests Avoid generating skolems just to return true Simplify adaptType by introducing unboxedType Update checkfiles is success & rm out file Redo & organise adaptType in SpaceEngine Emit deferred reachability warnings Fix typo Make trace typed, to avoid needless casting Revert "Bump tasty to 28.3-1" Dedupe ConsoleReporter & ReplConsoleReporter Implement ReplConsoleReporter in terms of the ReplDriver `out` PrintStream Fix and reinstate repl/errmsgs Fix printing refined self-types
2 parents 69870ce + 14c0875 commit 76b1805

File tree

13 files changed

+156
-78
lines changed

13 files changed

+156
-78
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1731,6 +1731,20 @@ class Definitions {
17311731
else sys.error(s"Not a primitive value type: $tp")
17321732
}.typeRef
17331733

1734+
def unboxedType(tp: Type)(using Context): TypeRef = {
1735+
val cls = tp.classSymbol
1736+
if (cls eq BoxedByteClass) ByteType
1737+
else if (cls eq BoxedShortClass) ShortType
1738+
else if (cls eq BoxedCharClass) CharType
1739+
else if (cls eq BoxedIntClass) IntType
1740+
else if (cls eq BoxedLongClass) LongType
1741+
else if (cls eq BoxedFloatClass) FloatType
1742+
else if (cls eq BoxedDoubleClass) DoubleType
1743+
else if (cls eq BoxedUnitClass) UnitType
1744+
else if (cls eq BoxedBooleanClass) BooleanType
1745+
else sys.error(s"Not a boxed primitive value type: $tp")
1746+
}
1747+
17341748
/** The JVM tag for `tp` if it's a primitive, `java.lang.Object` otherwise. */
17351749
def typeTag(tp: Type)(using Context): Name = typeTags(scalaClassName(tp))
17361750

compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,30 @@ import Diagnostic.{ Error, ConditionalWarning }
1212
class ConsoleReporter(
1313
reader: BufferedReader = Console.in,
1414
writer: PrintWriter = new PrintWriter(Console.err, true)
15-
) extends AbstractReporter {
15+
) extends ConsoleReporter.AbstractConsoleReporter {
16+
override def printMessage(msg: String): Unit = { writer.print(msg + "\n"); writer.flush() }
17+
override def flush()(using Context): Unit = writer.flush()
1618

17-
import Diagnostic._
18-
19-
/** Prints the message. */
20-
def printMessage(msg: String): Unit = { writer.print(msg + "\n"); writer.flush() }
21-
22-
/** Prints the message with the given position indication. */
23-
def doReport(dia: Diagnostic)(using Context): Unit = {
19+
override def doReport(dia: Diagnostic)(using Context): Unit = {
20+
super.doReport(dia)
2421
dia match
25-
case dia: Error =>
26-
printMessage(messageAndPos(dia))
27-
if (ctx.settings.Xprompt.value) Reporter.displayPrompt(reader, writer)
28-
case dia =>
29-
printMessage(messageAndPos(dia))
30-
31-
if shouldExplain(dia) then
32-
printMessage(explanation(dia.msg))
33-
else if dia.msg.canExplain then
34-
printMessage("\nlonger explanation available when compiling with `-explain`")
22+
case dia: Error if ctx.settings.Xprompt.value => Reporter.displayPrompt(reader, writer)
23+
case _ =>
3524
}
25+
}
26+
27+
object ConsoleReporter {
28+
abstract class AbstractConsoleReporter extends AbstractReporter {
29+
/** Prints the message. */
30+
def printMessage(msg: String): Unit
3631

37-
override def flush()(using Context): Unit = { writer.flush() }
32+
/** Prints the message with the given position indication. */
33+
def doReport(dia: Diagnostic)(using Context): Unit = {
34+
printMessage(messageAndPos(dia))
35+
if Diagnostic.shouldExplain(dia) then
36+
printMessage(explanation(dia.msg))
37+
else if dia.msg.canExplain then
38+
printMessage("\nlonger explanation available when compiling with `-explain`")
39+
}
40+
}
3841
}

compiler/src/dotty/tools/dotc/reporting/trace.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ trait TraceSyntax:
3333
apply(question, if cond then Printers.default else Printers.noPrinter, show)(op)
3434
else op
3535

36-
inline def apply[T](inline question: String, inline printer: Printers.Printer, inline showOp: Any => String)(inline op: T)(using Context): T =
36+
inline def apply[T, U >: T](inline question: String, inline printer: Printers.Printer, inline showOp: U => String)(inline op: T)(using Context): T =
3737
inline if isEnabled then
3838
doTrace[T](question, printer, showOp)(op)
3939
else op
@@ -60,15 +60,15 @@ trait TraceSyntax:
6060

6161
private def doTrace[T](question: => String,
6262
printer: Printers.Printer = Printers.default,
63-
showOp: Any => String = alwaysToString)
63+
showOp: T => String = alwaysToString)
6464
(op: => T)(using Context): T =
6565
if ctx.mode.is(Mode.Printing) || !isForced && (printer eq Printers.noPrinter) then op
6666
else
6767
// Avoid evaluating question multiple time, since each evaluation
6868
// may cause some extra logging output.
6969
val q = question
7070
val leading = s"==> $q?"
71-
val trailing = (res: Any) => s"<== $q = ${showOp(res)}"
71+
val trailing = (res: T) => s"<== $q = ${showOp(res)}"
7272
var finalized = false
7373
var logctx = ctx
7474
while logctx.reporter.isInstanceOf[StoreReporter] do logctx = logctx.outer

compiler/src/dotty/tools/dotc/transform/init/Semantic.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ class Semantic {
321321
def widenArgs: List[Value] = values.map(_.widenArg).toList
322322

323323
extension (value: Value)
324-
def select(field: Symbol, source: Tree, needResolve: Boolean = true): Contextual[Result] = log("select " + field.show, printer, res => res.asInstanceOf[Result].show) {
324+
def select(field: Symbol, source: Tree, needResolve: Boolean = true): Contextual[Result] = log("select " + field.show, printer, (_: Result).show) {
325325
if promoted.isCurrentObjectPromoted then Result(Hot, Nil)
326326
else value match {
327327
case Hot =>
@@ -371,7 +371,7 @@ class Semantic {
371371
}
372372
}
373373

374-
def call(meth: Symbol, args: List[ArgInfo], superType: Type, source: Tree, needResolve: Boolean = true): Contextual[Result] = log("call " + meth.show + ", args = " + args, printer, res => res.asInstanceOf[Result].show) {
374+
def call(meth: Symbol, args: List[ArgInfo], superType: Type, source: Tree, needResolve: Boolean = true): Contextual[Result] = log("call " + meth.show + ", args = " + args, printer, (_: Result).show) {
375375
def checkArgs = args.flatMap(_.promote)
376376

377377
// fast track if the current object is already initialized
@@ -445,7 +445,7 @@ class Semantic {
445445
}
446446

447447
/** Handle a new expression `new p.C` where `p` is abstracted by `value` */
448-
def instantiate(klass: ClassSymbol, ctor: Symbol, args: List[ArgInfo], source: Tree): Contextual[Result] = log("instantiating " + klass.show + ", value = " + value + ", args = " + args, printer, res => res.asInstanceOf[Result].show) {
448+
def instantiate(klass: ClassSymbol, ctor: Symbol, args: List[ArgInfo], source: Tree): Contextual[Result] = log("instantiating " + klass.show + ", value = " + value + ", args = " + args, printer, (_: Result).show) {
449449
val trace1 = trace.add(source)
450450
if promoted.isCurrentObjectPromoted then Result(Hot, Nil)
451451
else value match {
@@ -702,7 +702,7 @@ class Semantic {
702702
*
703703
* This method only handles cache logic and delegates the work to `cases`.
704704
*/
705-
def eval(expr: Tree, thisV: Addr, klass: ClassSymbol, cacheResult: Boolean = false): Contextual[Result] = log("evaluating " + expr.show + ", this = " + thisV.show, printer, res => res.asInstanceOf[Result].show) {
705+
def eval(expr: Tree, thisV: Addr, klass: ClassSymbol, cacheResult: Boolean = false): Contextual[Result] = log("evaluating " + expr.show + ", this = " + thisV.show, printer, (_: Result).show) {
706706
val innerMap = cache.getOrElseUpdate(thisV, new EqHashMap[Tree, Value])
707707
if (innerMap.contains(expr)) Result(innerMap(expr), Errors.empty)
708708
else {
@@ -911,7 +911,7 @@ class Semantic {
911911
}
912912

913913
/** Handle semantics of leaf nodes */
914-
def cases(tp: Type, thisV: Addr, klass: ClassSymbol, source: Tree): Contextual[Result] = log("evaluating " + tp.show, printer, res => res.asInstanceOf[Result].show) {
914+
def cases(tp: Type, thisV: Addr, klass: ClassSymbol, source: Tree): Contextual[Result] = log("evaluating " + tp.show, printer, (_: Result).show) {
915915
tp match {
916916
case _: ConstantType =>
917917
Result(Hot, Errors.empty)
@@ -941,7 +941,7 @@ class Semantic {
941941
}
942942

943943
/** Resolve C.this that appear in `klass` */
944-
def resolveThis(target: ClassSymbol, thisV: Value, klass: ClassSymbol, source: Tree): Contextual[Value] = log("resolving " + target.show + ", this = " + thisV.show + " in " + klass.show, printer, res => res.asInstanceOf[Value].show) {
944+
def resolveThis(target: ClassSymbol, thisV: Value, klass: ClassSymbol, source: Tree): Contextual[Value] = log("resolving " + target.show + ", this = " + thisV.show + " in " + klass.show, printer, (_: Value).show) {
945945
if target == klass then thisV
946946
else if target.is(Flags.Package) then Hot
947947
else
@@ -969,7 +969,7 @@ class Semantic {
969969
*
970970
* See `tpd.outerSelect` and `ElimOuterSelect`.
971971
*/
972-
def resolveOuterSelect(target: ClassSymbol, thisV: Value, hops: Int, source: Tree): Contextual[Value] = log("resolving outer " + target.show + ", this = " + thisV.show + ", hops = " + hops, printer, res => res.asInstanceOf[Value].show) {
972+
def resolveOuterSelect(target: ClassSymbol, thisV: Value, hops: Int, source: Tree): Contextual[Value] = log("resolving outer " + target.show + ", this = " + thisV.show + ", hops = " + hops, printer, (_: Value).show) {
973973
// Is `target` reachable from `cls` with the given `hops`?
974974
def reachable(cls: ClassSymbol, hops: Int): Boolean =
975975
if hops == 0 then cls == target
@@ -1011,7 +1011,7 @@ class Semantic {
10111011
else cases(tref.prefix, thisV, klass, source)
10121012

10131013
/** Initialize part of an abstract object in `klass` of the inheritance chain */
1014-
def init(tpl: Template, thisV: Addr, klass: ClassSymbol): Contextual[Result] = log("init " + klass.show, printer, res => res.asInstanceOf[Result].show) {
1014+
def init(tpl: Template, thisV: Addr, klass: ClassSymbol): Contextual[Result] = log("init " + klass.show, printer, (_: Result).show) {
10151015
val errorBuffer = new mutable.ArrayBuffer[Error]
10161016

10171017
val paramsMap = tpl.constr.termParamss.flatten.map { vdef =>

compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ trait SpaceLogic {
114114
def show(sp: Space): String
115115

116116
/** Simplify space such that a space equal to `Empty` becomes `Empty` */
117-
def simplify(space: Space)(using Context): Space = trace(s"simplify ${show(space)} --> ", debug, x => show(x.asInstanceOf[Space]))(space match {
117+
def simplify(space: Space)(using Context): Space = trace(s"simplify ${show(space)} --> ", debug, show)(space match {
118118
case Prod(tp, fun, spaces) =>
119119
val sps = spaces.map(simplify(_))
120120
if (sps.contains(Empty)) Empty
@@ -194,7 +194,7 @@ trait SpaceLogic {
194194
}
195195

196196
/** Intersection of two spaces */
197-
def intersect(a: Space, b: Space)(using Context): Space = trace(s"${show(a)} & ${show(b)}", debug, x => show(x.asInstanceOf[Space])) {
197+
def intersect(a: Space, b: Space)(using Context): Space = trace(s"${show(a)} & ${show(b)}", debug, show) {
198198
def tryDecompose1(tp: Type) = intersect(Or(decompose(tp)), b)
199199
def tryDecompose2(tp: Type) = intersect(a, Or(decompose(tp)))
200200

@@ -226,7 +226,7 @@ trait SpaceLogic {
226226
}
227227

228228
/** The space of a not covered by b */
229-
def minus(a: Space, b: Space)(using Context): Space = trace(s"${show(a)} - ${show(b)}", debug, x => show(x.asInstanceOf[Space])) {
229+
def minus(a: Space, b: Space)(using Context): Space = trace(s"${show(a)} - ${show(b)}", debug, show) {
230230
def tryDecompose1(tp: Type) = minus(Or(decompose(tp)), b)
231231
def tryDecompose2(tp: Type) = minus(a, Or(decompose(tp)))
232232

@@ -512,23 +512,28 @@ class SpaceEngine(using Context) extends SpaceLogic {
512512
if converted == null then tp else ConstantType(converted)
513513
case _ => tp
514514

515-
/** Adapt types by performing primitive value boxing. #12805 */
516-
def maybeBox(tp1: Type, tp2: Type): Type =
517-
if tp1.classSymbol.isPrimitiveValueClass && !tp2.classSymbol.isPrimitiveValueClass then
518-
defn.boxedType(tp1).narrow
519-
else tp1
515+
def isPrimToBox(tp: Type, pt: Type) =
516+
tp.classSymbol.isPrimitiveValueClass && (defn.boxedType(tp).classSymbol eq pt.classSymbol)
517+
518+
/** Adapt types by performing primitive value unboxing or boxing, or numeric constant conversion. #12805
519+
*
520+
* This makes these isSubType cases work like this:
521+
* {{{
522+
* 1 <:< Integer => (<skolem> : Integer) <:< Integer = true
523+
* ONE <:< Int => (<skolem> : Int) <:< Int = true
524+
* Integer <:< (1: Int) => (<skolem> : Int) <:< (1: Int) = false
525+
* }}}
526+
*/
527+
def adaptType(tp1: Type, tp2: Type): Type = trace(i"adaptType($tp1, $tp2)", show = true) {
528+
if isPrimToBox(tp1, tp2) then defn.boxedType(tp1).narrow
529+
else if isPrimToBox(tp2, tp1) then defn.unboxedType(tp1).narrow
530+
else convertConstantType(tp1, tp2)
531+
}
520532

521533
/** Is `tp1` a subtype of `tp2`? */
522-
def isSubType(_tp1: Type, tp2: Type): Boolean = {
523-
val tp1 = maybeBox(convertConstantType(_tp1, tp2), tp2)
524-
//debug.println(TypeComparer.explained(_.isSubType(tp1, tp2)))
525-
val res = if (ctx.explicitNulls) {
526-
tp1 <:< tp2
527-
} else {
528-
(tp1 != constantNullType || tp2 == constantNullType) && tp1 <:< tp2
529-
}
530-
debug.println(i"$tp1 <:< $tp2 = $res")
531-
res
534+
def isSubType(tp1: Type, tp2: Type): Boolean = trace(i"$tp1 <:< $tp2", debug, show = true) {
535+
if tp1 == constantNullType && !ctx.explicitNulls then tp2 == constantNullType
536+
else adaptType(tp1, tp2) <:< tp2
532537
}
533538

534539
def isSameUnapply(tp1: TermRef, tp2: TermRef): Boolean =
@@ -900,7 +905,9 @@ class SpaceEngine(using Context) extends SpaceLogic {
900905
def checkRedundancy(_match: Match): Unit = {
901906
debug.println(s"---------------checking redundant patterns ${_match.show}")
902907

903-
val Match(sel, cases) = _match
908+
val Match(sel, _) = _match
909+
val cases = _match.cases.toIndexedSeq
910+
904911
val selTyp = sel.tpe.widen.dealias
905912

906913
if (!redundancyCheckable(sel)) return
@@ -911,7 +918,14 @@ class SpaceEngine(using Context) extends SpaceLogic {
911918
else project(selTyp)
912919
debug.println(s"targetSpace: ${show(targetSpace)}")
913920

914-
cases.iterator.zipWithIndex.foldLeft(Nil: List[Space]) { case (prevs, (CaseDef(pat, guard, _), i)) =>
921+
var i = 0
922+
val len = cases.length
923+
var prevs = List.empty[Space]
924+
var deferred = List.empty[Tree]
925+
926+
while (i < len) {
927+
val CaseDef(pat, guard, _) = cases(i)
928+
915929
debug.println(i"case pattern: $pat")
916930

917931
val curr = project(pat)
@@ -923,18 +937,24 @@ class SpaceEngine(using Context) extends SpaceLogic {
923937
val covered = simplify(intersect(curr, targetSpace))
924938
debug.println(s"covered: ${show(covered)}")
925939

926-
if pat != EmptyTree // rethrow case of catch uses EmptyTree
927-
&& prev != Empty // avoid isSubspace(Empty, Empty) - one of the previous cases much be reachable
928-
&& isSubspace(covered, prev)
929-
then {
930-
if isNullable && i == cases.length - 1 && isWildcardArg(pat) then
931-
report.warning(MatchCaseOnlyNullWarning(), pat.srcPos)
932-
else
940+
if prev == Empty && covered == Empty then // defer until a case is reachable
941+
deferred ::= pat
942+
else {
943+
for (pat <- deferred.reverseIterator)
933944
report.warning(MatchCaseUnreachable(), pat.srcPos)
945+
if pat != EmptyTree // rethrow case of catch uses EmptyTree
946+
&& isSubspace(covered, prev)
947+
then {
948+
val nullOnly = isNullable && i == len - 1 && isWildcardArg(pat)
949+
val msg = if nullOnly then MatchCaseOnlyNullWarning() else MatchCaseUnreachable()
950+
report.warning(msg, pat.srcPos)
951+
}
952+
deferred = Nil
934953
}
935954

936955
// in redundancy check, take guard as false in order to soundly approximate
937-
(if guard.isEmpty then covered else Empty) :: prevs
956+
prevs ::= (if guard.isEmpty then covered else Empty)
957+
i += 1
938958
}
939959
}
940960
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ object PrepareInlineable {
8080
def preTransform(tree: Tree)(using Context): Tree = tree match {
8181
case tree: RefTree if needsAccessor(tree.symbol) =>
8282
if (tree.symbol.isConstructor) {
83-
report.error("Implementation restriction: cannot use private constructors in inlineinline methods", tree.srcPos)
83+
report.error("Implementation restriction: cannot use private constructors in inline methods", tree.srcPos)
8484
tree // TODO: create a proper accessor for the private constructor
8585
}
8686
else useAccessor(tree)

compiler/src/dotty/tools/repl/ReplDriver.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package dotty.tools.repl
22

3-
import java.io.{File => JFile, PrintStream, PrintWriter}
3+
import java.io.{File => JFile, PrintStream}
44
import java.nio.charset.StandardCharsets
55

66
import dotty.tools.dotc.ast.Trees._
@@ -425,12 +425,12 @@ class ReplDriver(settings: Array[String],
425425
state
426426
}
427427

428-
/** Like ConsoleReporter, but without file paths or real -Xprompt'ing */
429-
private object ReplConsoleReporter extends ConsoleReporter(
430-
reader = null, // this short-circuits the -Xprompt display from waiting for an input
431-
writer = new PrintWriter(out, /* autoFlush = */ true), // write to out, not Console.err
432-
) {
428+
/** Like ConsoleReporter, but without file paths, -Xprompt displaying,
429+
* and using a PrintStream rather than a PrintWriter so messages aren't re-encoded. */
430+
private object ReplConsoleReporter extends ConsoleReporter.AbstractConsoleReporter {
433431
override def posFileStr(pos: SourcePosition) = "" // omit file paths
432+
override def printMessage(msg: String): Unit = out.println(msg)
433+
override def flush()(using Context): Unit = out.flush()
434434
}
435435

436436
/** Print warnings & errors using ReplConsoleReporter, and info straight to out */

compiler/test-resources/pending/repl/errmsgs renamed to compiler/test-resources/repl/errmsgs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,19 +51,19 @@ scala> abstract class C { type T; val x: T; val s: Unit = { type T = String; var
5151
1 | abstract class C { type T; val x: T; val s: Unit = { type T = String; var y: T = x; locally { def f() = { type T = Int; val z: T = y }; f() } }; }
5252
| ^
5353
|Found: (C.this.x : C.this.T)
54-
|Required: T?
54+
|Required: T²
5555
|
5656
|where: T is a type in class C
57-
| T? is a type in the initializer of value s which is an alias of String
57+
| T² is a type in the initializer of value s which is an alias of String
5858
longer explanation available when compiling with `-explain`
5959
-- [E007] Type Mismatch Error: -------------------------------------------------
6060
1 | abstract class C { type T; val x: T; val s: Unit = { type T = String; var y: T = x; locally { def f() = { type T = Int; val z: T = y }; f() } }; }
6161
| ^
6262
|Found: (y : T)
63-
|Required: T?
63+
|Required: T²
6464
|
6565
|where: T is a type in the initializer of value s which is an alias of String
66-
| T? is a type in method f which is an alias of Int
66+
| T² is a type in method f which is an alias of Int
6767
longer explanation available when compiling with `-explain`
6868
2 errors found
6969
scala> class Foo() { def bar: Int = 1 }; val foo = new Foo(); foo.barr
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package dotty.tools
2+
package dotc
3+
package transform
4+
5+
import org.junit.*, Assert.*
6+
7+
import core.*, Constants.*, Contexts.*, Decorators.*, Symbols.*, Types.*
8+
9+
class SpaceEngineTest extends DottyTest:
10+
@Test def testAdaptTest(): Unit =
11+
given Context = ctx
12+
val defn = ctx.definitions
13+
import defn._
14+
val e = patmat.SpaceEngine()
15+
16+
val BoxedIntType = BoxedIntClass.typeRef
17+
val ConstOneType = ConstantType(Constant(1))
18+
19+
assertTrue(e.isPrimToBox(IntType, BoxedIntType))
20+
assertFalse(e.isPrimToBox(BoxedIntType, IntType))
21+
assertTrue(e.isPrimToBox(ConstOneType, BoxedIntType))
22+
23+
assertEquals(BoxedIntType, e.adaptType(IntType, BoxedIntType).widenSingleton)
24+
assertEquals(IntType, e.adaptType(BoxedIntType, IntType).widenSingleton)
25+
assertEquals(IntType, e.adaptType(BoxedIntType, ConstOneType).widenSingleton)

0 commit comments

Comments
 (0)