From 60b513d54809ba0047148d8a192dd9980cce9831 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 11 Apr 2021 17:04:20 +0200 Subject: [PATCH 1/9] Explain match type reduction failures in error messages --- .../tools/dotc/core/MatchTypeTrace.scala | 86 +++++++++++++++++++ .../dotty/tools/dotc/core/TypeComparer.scala | 18 +++- .../src/dotty/tools/dotc/core/Types.scala | 11 ++- .../tools/dotc/printing/PlainPrinter.scala | 6 +- .../dotty/tools/dotc/reporting/Message.scala | 5 +- .../dotty/tools/dotc/reporting/messages.scala | 11 +++ tests/neg/i12049.scala | 7 ++ 7 files changed, 133 insertions(+), 11 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala create mode 100644 tests/neg/i12049.scala diff --git a/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala b/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala new file mode 100644 index 000000000000..4ffff61042bf --- /dev/null +++ b/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala @@ -0,0 +1,86 @@ +package dotty.tools +package dotc +package core + +import Types._, Contexts._, Symbols._, Decorators._ +import util.Property + +object MatchTypeTrace: + private enum TraceEntry: + case TryReduce(scrut: Type) + case NoMatches(scrut: Type, cases: List[Type]) + case Stuck(scrut: Type, stuckCase: Type, otherCases: List[Type]) + import TraceEntry._ + + private class MatchTrace: + var entries: List[TraceEntry] = Nil + + private val MatchTrace = new Property.Key[MatchTrace] + + def record(op: Context ?=> Any)(using Context): String = + val trace = new MatchTrace + inContext(ctx.fresh.setProperty(MatchTrace, trace)) { + op + if trace.entries.isEmpty then "" + else + i""" + | + |Note: a match type could not be fully reduced: + | + |${trace.entries.reverse.map(explainEntry)}%\n%""" + } + + def isRecording(using Context): Boolean = + ctx.property(MatchTrace).isDefined + + private def matchTypeFail(entry: TraceEntry)(using Context) = + ctx.property(MatchTrace) match + case Some(trace) => + trace.entries match + case (e: TryReduce) :: es => trace.entries = entry :: trace.entries + case _ => + case _ => + + def noMatches(scrut: Type, cases: List[Type])(using Context) = + matchTypeFail(NoMatches(scrut, cases)) + + def stuck(scrut: Type, stuckCase: Type, otherCases: List[Type])(using Context) = + matchTypeFail(Stuck(scrut, stuckCase, otherCases)) + + def recurseWith(scrut: Type)(op: => Type)(using Context): Type = + ctx.property(MatchTrace) match + case Some(trace) => + val prev = trace.entries + trace.entries = TryReduce(scrut) :: prev + val res = op + if res.exists then trace.entries = prev + res + case _ => + op + + private def caseText(tp: Type)(using Context): String = tp match + case tp: HKTypeLambda => caseText(tp.resultType) + case defn.MatchCase(pat, body) => i"case $pat => $body" + case _ => i"case $tp" + + private def casesText(cases: List[Type])(using Context) = + i"${cases.map(caseText)}%\n %" + + private def explainEntry(entry: TraceEntry)(using Context): String = entry match + case TryReduce(scrut: Type) => + i" trying to reduce $scrut" + case NoMatches(scrut, cases) => + i""" failed since selector $scrut + | matches none of the cases + | + | ${casesText(cases)}""" + case Stuck(scrut, stuckCase, otherCases) => + i""" failed since selector $scrut + | does not match ${caseText(stuckCase)} + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case${if otherCases.length == 1 then "" else "s"} + | + | ${casesText(otherCases)}""" + +end MatchTypeTrace + diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 62b06aea39a7..d5b67c9da935 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -2798,10 +2798,20 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) { Some(NoType) } - def recur(cases: List[Type]): Type = cases match { - case cas :: cases1 => matchCase(cas).getOrElse(recur(cases1)) - case Nil => NoType - } + def recur(remaining: List[Type]): Type = remaining match + case cas :: remaining1 => + matchCase(cas) match + case None => + recur(remaining1) + case Some(NoType) => + if remaining1.isEmpty then MatchTypeTrace.noMatches(scrut, cases) + else MatchTypeTrace.stuck(scrut, cas, remaining1) + NoType + case Some(tp) => + tp + case Nil => + MatchTypeTrace.noMatches(scrut, cases) + NoType inFrozenConstraint { // Empty types break the basic assumption that if a scrutinee and a diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index f9cbf932f9c8..581ac48705f7 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -4025,7 +4025,9 @@ object Types { def tryMatchAlias = tycon.info match { case MatchAlias(alias) => trace(i"normalize $this", typr, show = true) { - alias.applyIfParameterized(args).tryNormalize + MatchTypeTrace.recurseWith(this) { + alias.applyIfParameterized(args).tryNormalize + } } case _ => NoType @@ -4537,7 +4539,11 @@ object Types { } record("MatchType.reduce called") - if (!Config.cacheMatchReduced || myReduced == null || !isUpToDate) { + if !Config.cacheMatchReduced + || myReduced == null + || !isUpToDate + || MatchTypeTrace.isRecording + then record("MatchType.reduce computed") if (myReduced != null) record("MatchType.reduce cache miss") myReduced = @@ -4549,7 +4555,6 @@ object Types { finally updateReductionContext(cmp.footprint) TypeComparer.tracked(matchCases) } - } myReduced } diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 618f7be6dac3..7092906ad11e 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -182,9 +182,9 @@ class PlainPrinter(_ctx: Context) extends Printer { case _ => "case " ~ toText(tp) } def casesText = Text(cases.map(caseText), "\n") - atPrec(InfixPrec) { toText(scrutinee) } ~ - keywordStr(" match ") ~ "{" ~ casesText ~ "}" ~ - (" <: " ~ toText(bound) provided !bound.isAny) + atPrec(InfixPrec) { toText(scrutinee) } ~ + keywordStr(" match ") ~ "{" ~ casesText ~ "}" ~ + (" <: " ~ toText(bound) provided !bound.isAny) }.close case tp: PreviousErrorType if ctx.settings.XprintTypes.value => "" // do not print previously reported error message because they may try to print this error type again recuresevely diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index 03dc0426c582..bed1d955c1a4 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -58,6 +58,9 @@ abstract class Message(val errorId: ErrorMessageID) { self => */ protected def explain: String + /** A message suffix that can be added for certain subclasses */ + protected def msgSuffix: String = "" + /** Does this message have an explanation? * This is normally the same as `explain.nonEmpty` but can be overridden * if we need a way to return `true` without actually calling the @@ -82,7 +85,7 @@ abstract class Message(val errorId: ErrorMessageID) { self => def rawMessage = message /** The message to report. tags are filtered out */ - lazy val message: String = dropNonSensical(msg) + lazy val message: String = dropNonSensical(msg + msgSuffix) /** The explanation to report. tags are filtered out */ lazy val explanation: String = dropNonSensical(explain) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 3ef9d8d076a2..da6c81f80dd8 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -50,6 +50,17 @@ import transform.SymUtils._ def explain = err.whyNoMatchStr(found, expected) override def canExplain = true + override def msgSuffix: String = + val collectMatchTrace = new TypeAccumulator[String]: + def apply(s: String, tp: Type): String = + if s.nonEmpty then s + else tp match + case tp: AppliedType if tp.isMatchAlias => MatchTypeTrace.record(tp.tryNormalize) + case tp: MatchType => MatchTypeTrace.record(tp.tryNormalize) + case _ => foldOver(s, tp) + collectMatchTrace(collectMatchTrace("", found), expected) + end TypeMismatchMsg + abstract class NamingMsg(errorId: ErrorMessageID) extends Message(errorId): def kind = "Naming" diff --git a/tests/neg/i12049.scala b/tests/neg/i12049.scala new file mode 100644 index 000000000000..a71040a64a13 --- /dev/null +++ b/tests/neg/i12049.scala @@ -0,0 +1,7 @@ +trait A +trait B +type M[X] = X match + case A => Int + case B => String +val x: String = ??? : M[B] + From d77f3eacd4ba2fbd226fcaafd86c9ed5c713841f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 11 Apr 2021 21:31:10 +0200 Subject: [PATCH 2/9] Fix printing of match type cases defining type variables --- compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 7092906ad11e..d76e3b67638a 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -178,6 +178,7 @@ class PlainPrinter(_ctx: Context) extends Printer { case MatchType(bound, scrutinee, cases) => changePrec(GlobalPrec) { def caseText(tp: Type): Text = tp match { + case tp: HKTypeLambda => caseText(tp.resultType) case defn.MatchCase(pat, body) => "case " ~ toText(pat) ~ " => " ~ toText(body) case _ => "case " ~ toText(tp) } From e6c5b18f123884024774565859d08430a6910843 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 11 Apr 2021 21:32:18 +0200 Subject: [PATCH 3/9] Don't print package objects in prefixes --- .../src/dotty/tools/dotc/printing/RefinedPrinter.scala | 7 ++++--- tests/neg/i7056.check | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index cf60a152547b..14def1edde91 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -105,15 +105,16 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { override def toTextPrefix(tp: Type): Text = controlled { def isOmittable(sym: Symbol) = - if (printDebug) false - else if (homogenizedView) isEmptyPrefix(sym) // drop and anonymous classes, but not scala, Predef. + if printDebug then false + else if homogenizedView then isEmptyPrefix(sym) // drop and anonymous classes, but not scala, Predef. + else if sym.isPackageObject then isOmittablePrefix(sym.owner) else isOmittablePrefix(sym) tp match { case tp: ThisType if isOmittable(tp.cls) => "" case tp @ TermRef(pre, _) => val sym = tp.symbol - if (sym.isPackageObject && !homogenizedView) toTextPrefix(pre) + if sym.isPackageObject && !homogenizedView && !printDebug then toTextPrefix(pre) else if (isOmittable(sym)) "" else super.toTextPrefix(tp) case _ => super.toTextPrefix(tp) diff --git a/tests/neg/i7056.check b/tests/neg/i7056.check index 2dff4dc66b13..1695c1e4b6f0 100644 --- a/tests/neg/i7056.check +++ b/tests/neg/i7056.check @@ -4,4 +4,4 @@ | value idnt1 is not a member of B. | An extension method was tried, but could not be fully constructed: | - | i7056$package.given_T1_T[T](given_PartialId_B).idnt1() + | given_T1_T[T](given_PartialId_B).idnt1() From df6f80c435906aa403f5540901c5d49cab38e2b8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 11 Apr 2021 21:33:45 +0200 Subject: [PATCH 4/9] Don't print LazyRef nodes unless in -Yprint-debug --- compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 14def1edde91..dcd6564d192d 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -241,6 +241,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { toTextParents(tp.parents) ~~ "{...}" case JavaArrayType(elemtp) => toText(elemtp) ~ "[]" + case tp: LazyRef if !printDebug => + try toText(tp.ref) + catch case ex: Throwable => "..." case tp: SelectionProto => "?{ " ~ toText(tp.name) ~ (Str(" ") provided !tp.name.toSimpleName.last.isLetterOrDigit) ~ From 1857dc9b80bcbba0e1839f7d362bba7d4f97b1fb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 11 Apr 2021 21:38:43 +0200 Subject: [PATCH 5/9] Add test --- tests/neg/i12049.check | 50 ++++++++++++++++++++++++++++++++++++++++++ tests/neg/i12049.scala | 17 +++++++++++++- 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 tests/neg/i12049.check diff --git a/tests/neg/i12049.check b/tests/neg/i12049.check new file mode 100644 index 000000000000..378fa00c8499 --- /dev/null +++ b/tests/neg/i12049.check @@ -0,0 +1,50 @@ +-- [E007] Type Mismatch Error: tests/neg/i12049.scala:6:16 ------------------------------------------------------------- +6 |val x: String = ??? : M[B] // error + | ^^^^^^^^^^ + | Found: M[B] + | Required: String + | + | Note: a match type could not be fully reduced: + | + | trying to reduce M[B] + | failed since selector B + | does not match case A => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case B => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/i12049.scala:14:17 ------------------------------------------------------------ +14 |val y3: String = ??? : Last[Int *: Int *: Boolean *: String *: EmptyTuple] // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Found: Last[EmptyTuple.type] + | Required: String + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Last[EmptyTuple.type] + | failed since selector EmptyTuple.type + | matches none of the cases + | + | case _ *: _ *: t => Last[t] + | case t *: EmptyTuple => t + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/i12049.scala:22:20 ------------------------------------------------------------ +22 |val z3: (A, B, A) = ??? : Reverse[(A, B, A)] // error + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | Found: Tuple.Concat[Reverse[A *: EmptyTuple.type], (B, A)] + | Required: (A, B, A) + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Tuple.Concat[Reverse[A *: EmptyTuple.type], (B, A)] + | trying to reduce Reverse[A *: EmptyTuple.type] + | failed since selector A *: EmptyTuple.type + | matches none of the cases + | + | case t1 *: t2 *: ts => Tuple.Concat[Reverse[ts], (t2, t1)] + | case EmptyTuple => EmptyTuple + +longer explanation available when compiling with `-explain` diff --git a/tests/neg/i12049.scala b/tests/neg/i12049.scala index a71040a64a13..e3c81e3d0c89 100644 --- a/tests/neg/i12049.scala +++ b/tests/neg/i12049.scala @@ -3,5 +3,20 @@ trait B type M[X] = X match case A => Int case B => String -val x: String = ??? : M[B] +val x: String = ??? : M[B] // error +type Last[X <: Tuple] = X match + case _ *: _ *: t => Last[t] + case t *: EmptyTuple => t + +val y1: Int = ??? : Last[Int *: EmptyTuple] +val y2: String = ??? : Last[Int *: Boolean *: String *: EmptyTuple] +val y3: String = ??? : Last[Int *: Int *: Boolean *: String *: EmptyTuple] // error + +type Reverse[X <: Tuple] = X match + case t1 *: t2 *: ts => Tuple.Concat[Reverse[ts], (t2, t1)] + case EmptyTuple => EmptyTuple + +val z1: (B, A) = ??? : Reverse[(A, B)] +val z2: (B, A, B, A) = ??? : Reverse[(A, B, A, B)] +val z3: (A, B, A) = ??? : Reverse[(A, B, A)] // error From bd43e9979445899d11bad9307376c418821869ab Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 11 Apr 2021 22:24:29 +0200 Subject: [PATCH 6/9] Show match type reduction failures for other errors Also show match type reduction failures for implicit search errors and member not found errors. --- .../dotty/tools/dotc/reporting/messages.scala | 21 +++++++------------ .../tools/dotc/typer/ErrorReporting.scala | 14 ++++++++++++- tests/neg/i12049.scala | 7 +++++++ 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index da6c81f80dd8..51335e7a7abd 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -16,7 +16,7 @@ import printing.Formatting import ErrorMessageID._ import ast.Trees import config.{Feature, ScalaVersion} -import typer.ErrorReporting.err +import typer.ErrorReporting.{err, matchReductionAddendum} import typer.ProtoTypes.ViewProto import scala.util.control.NonFatal import StdNames.nme @@ -45,22 +45,15 @@ import transform.SymUtils._ abstract class TypeMsg(errorId: ErrorMessageID) extends Message(errorId): def kind = "Type" - abstract class TypeMismatchMsg(found: Type, expected: Type)(errorId: ErrorMessageID)(using Context) extends Message(errorId): + trait ShowMatchTrace(tps: Type*)(using Context) extends Message: + override def msgSuffix: String = matchReductionAddendum(tps*) + + abstract class TypeMismatchMsg(found: Type, expected: Type)(errorId: ErrorMessageID)(using Context) + extends Message(errorId), ShowMatchTrace(found, expected): def kind = "Type Mismatch" def explain = err.whyNoMatchStr(found, expected) override def canExplain = true - override def msgSuffix: String = - val collectMatchTrace = new TypeAccumulator[String]: - def apply(s: String, tp: Type): String = - if s.nonEmpty then s - else tp match - case tp: AppliedType if tp.isMatchAlias => MatchTypeTrace.record(tp.tryNormalize) - case tp: MatchType => MatchTypeTrace.record(tp.tryNormalize) - case _ => foldOver(s, tp) - collectMatchTrace(collectMatchTrace("", found), expected) - end TypeMismatchMsg - abstract class NamingMsg(errorId: ErrorMessageID) extends Message(errorId): def kind = "Naming" @@ -292,7 +285,7 @@ import transform.SymUtils._ end TypeMismatch class NotAMember(site: Type, val name: Name, selected: String, addendum: => String = "")(using Context) - extends NotFoundMsg(NotAMemberID) { + extends NotFoundMsg(NotAMemberID), ShowMatchTrace(site) { //println(i"site = $site, decls = ${site.decls}, source = ${site.typeSymbol.sourceFile}") //DEBUG def msg = { diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 413ae14762b8..2c5eaca1a4c5 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -51,6 +51,16 @@ object ErrorReporting { case _ => report.error(em"missing arguments for $meth", tree.srcPos) + def matchReductionAddendum(tps: Type*)(using Context): String = + val collectMatchTrace = new TypeAccumulator[String]: + def apply(s: String, tp: Type): String = + if s.nonEmpty then s + else tp match + case tp: AppliedType if tp.isMatchAlias => MatchTypeTrace.record(tp.tryNormalize) + case tp: MatchType => MatchTypeTrace.record(tp.tryNormalize) + case _ => foldOver(s, tp) + tps.foldLeft("")(collectMatchTrace) + class Errors(using Context) { /** An explanatory note to be added to error messages @@ -253,7 +263,9 @@ class ImplicitSearchError( val shortMessage = userDefinedImplicitNotFoundParamMessage .orElse(userDefinedImplicitNotFoundTypeMessage) .getOrElse(defaultImplicitNotFoundMessage) - formatMsg(shortMessage)() ++ hiddenImplicitsAddendum + formatMsg(shortMessage)() + ++ hiddenImplicitsAddendum + ++ ErrorReporting.matchReductionAddendum(pt) } private def formatMsg(shortForm: String)(headline: String = shortForm) = arg match { diff --git a/tests/neg/i12049.scala b/tests/neg/i12049.scala index e3c81e3d0c89..67b2d8801122 100644 --- a/tests/neg/i12049.scala +++ b/tests/neg/i12049.scala @@ -20,3 +20,10 @@ type Reverse[X <: Tuple] = X match val z1: (B, A) = ??? : Reverse[(A, B)] val z2: (B, A, B, A) = ??? : Reverse[(A, B, A, B)] val z3: (A, B, A) = ??? : Reverse[(A, B, A)] // error + +val _ = summon[M[B]] +val _ = summon[String =:= Last[Int *: Int *: Boolean *: String *: EmptyTuple]] +val _ = summon[(A, B, A) =:= Reverse[(A, B, A)]] + +val _ = (??? : M[B]).length + From 97e77a6d569466f1025754a27ee1f96936ccc9ee Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 11 Apr 2021 22:24:41 +0200 Subject: [PATCH 7/9] Fix tests and add check files --- tests/neg/i12049.check | 55 ++++ tests/neg/i12049.scala | 8 +- tests/neg/matchtype-seq.check | 481 ++++++++++++++++++++++++++++++++++ 3 files changed, 540 insertions(+), 4 deletions(-) create mode 100644 tests/neg/matchtype-seq.check diff --git a/tests/neg/i12049.check b/tests/neg/i12049.check index 378fa00c8499..adcccf51a4dc 100644 --- a/tests/neg/i12049.check +++ b/tests/neg/i12049.check @@ -48,3 +48,58 @@ longer explanation available when compiling with `-explain` | case EmptyTuple => EmptyTuple longer explanation available when compiling with `-explain` +-- Error: tests/neg/i12049.scala:24:20 --------------------------------------------------------------------------------- +24 |val _ = summon[M[B]] // error + | ^ + | no implicit argument of type M[B] was found for parameter x of method summon in object Predef + | + | Note: a match type could not be fully reduced: + | + | trying to reduce M[B] + | failed since selector B + | does not match case A => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case B => String +-- Error: tests/neg/i12049.scala:25:78 --------------------------------------------------------------------------------- +25 |val _ = summon[String =:= Last[Int *: Int *: Boolean *: String *: EmptyTuple]] // error + | ^ + | Cannot prove that String =:= Last[EmptyTuple.type]. + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Last[EmptyTuple.type] + | failed since selector EmptyTuple.type + | matches none of the cases + | + | case _ *: _ *: t => Last[t] + | case t *: EmptyTuple => t +-- Error: tests/neg/i12049.scala:26:48 --------------------------------------------------------------------------------- +26 |val _ = summon[(A, B, A) =:= Reverse[(A, B, A)]] // error + | ^ + | Cannot prove that (A, B, A) =:= Tuple.Concat[Reverse[A *: EmptyTuple.type], (B, A)]. + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Tuple.Concat[Reverse[A *: EmptyTuple.type], (B, A)] + | trying to reduce Reverse[A *: EmptyTuple.type] + | failed since selector A *: EmptyTuple.type + | matches none of the cases + | + | case t1 *: t2 *: ts => Tuple.Concat[Reverse[ts], (t2, t1)] + | case EmptyTuple => EmptyTuple +-- [E008] Not Found Error: tests/neg/i12049.scala:28:21 ---------------------------------------------------------------- +28 |val _ = (??? : M[B]).length // error + | ^^^^^^^^^^^^^^^^^^^ + | value length is not a member of M[B] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce M[B] + | failed since selector B + | does not match case A => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case B => String diff --git a/tests/neg/i12049.scala b/tests/neg/i12049.scala index 67b2d8801122..e69ac9e7a217 100644 --- a/tests/neg/i12049.scala +++ b/tests/neg/i12049.scala @@ -21,9 +21,9 @@ val z1: (B, A) = ??? : Reverse[(A, B)] val z2: (B, A, B, A) = ??? : Reverse[(A, B, A, B)] val z3: (A, B, A) = ??? : Reverse[(A, B, A)] // error -val _ = summon[M[B]] -val _ = summon[String =:= Last[Int *: Int *: Boolean *: String *: EmptyTuple]] -val _ = summon[(A, B, A) =:= Reverse[(A, B, A)]] +val _ = summon[M[B]] // error +val _ = summon[String =:= Last[Int *: Int *: Boolean *: String *: EmptyTuple]] // error +val _ = summon[(A, B, A) =:= Reverse[(A, B, A)]] // error -val _ = (??? : M[B]).length +val _ = (??? : M[B]).length // error diff --git a/tests/neg/matchtype-seq.check b/tests/neg/matchtype-seq.check new file mode 100644 index 000000000000..b5dc6923d374 --- /dev/null +++ b/tests/neg/matchtype-seq.check @@ -0,0 +1,481 @@ +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:9:18 ------------------------------------------------------ +9 | identity[T1[3]]("") // error + | ^^ + | Found: ("" : String) + | Required: Test.T1[(3 : Int)] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T1[(3 : Int)] + | failed since selector (3 : Int) + | matches none of the cases + | + | case (1 : Int) => Int + | case (2 : Int) => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:10:18 ----------------------------------------------------- +10 | identity[T1[3]](1) // error + | ^ + | Found: (1 : Int) + | Required: Test.T1[(3 : Int)] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T1[(3 : Int)] + | failed since selector (3 : Int) + | matches none of the cases + | + | case (1 : Int) => Int + | case (2 : Int) => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:11:20 ----------------------------------------------------- +11 | identity[T1[Int]]("") // error + | ^^ + | Found: ("" : String) + | Required: Test.T1[Int] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T1[Int] + | failed since selector Int + | does not match case (1 : Int) => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case (2 : Int) => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:12:20 ----------------------------------------------------- +12 | identity[T1[Int]](1) // error + | ^ + | Found: (1 : Int) + | Required: Test.T1[Int] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T1[Int] + | failed since selector Int + | does not match case (1 : Int) => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case (2 : Int) => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:21:20 ----------------------------------------------------- +21 | identity[T2[Int]]("") // error + | ^^ + | Found: ("" : String) + | Required: Test.T2[Int] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T2[Int] + | failed since selector Int + | does not match case (1 : Int) => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case _ => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:22:18 ----------------------------------------------------- +22 | identity[T2[2]](1) // error + | ^ + | Found: (1 : Int) + | Required: String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:23:20 ----------------------------------------------------- +23 | identity[T2[Int]](1) // error + | ^ + | Found: (1 : Int) + | Required: Test.T2[Int] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T2[Int] + | failed since selector Int + | does not match case (1 : Int) => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case _ => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:36:18 ----------------------------------------------------- +36 | identity[T3[A]](1) // error + | ^ + | Found: (1 : Int) + | Required: Test.T3[Test.A] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T3[Test.A] + | failed since selector Test.A + | does not match case Test.B => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case Test.C => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:37:18 ----------------------------------------------------- +37 | identity[T3[A]]("") // error + | ^^ + | Found: ("" : String) + | Required: Test.T3[Test.A] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T3[Test.A] + | failed since selector Test.A + | does not match case Test.B => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case Test.C => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:55:18 ----------------------------------------------------- +55 | identity[T5[A]](1) // error + | ^ + | Found: (1 : Int) + | Required: Test.T5[Test.A] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T5[Test.A] + | failed since selector Test.A + | does not match case Test.C => String + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case Test.A => Int + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:56:18 ----------------------------------------------------- +56 | identity[T5[A]]("") // error + | ^^ + | Found: ("" : String) + | Required: Test.T5[Test.A] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T5[Test.A] + | failed since selector Test.A + | does not match case Test.C => String + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case Test.A => Int + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:82:18 ----------------------------------------------------- +82 | identity[T7[D]]("") // error + | ^^ + | Found: ("" : String) + | Required: Test.T7[Test.D] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T7[Test.D] + | failed since selector Test.D + | does not match case Test.A2 => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case Test.D => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:83:18 ----------------------------------------------------- +83 | identity[T7[D]](1) // error + | ^ + | Found: (1 : Int) + | Required: Test.T7[Test.D] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T7[Test.D] + | failed since selector Test.D + | does not match case Test.A2 => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case Test.D => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:94:19 ----------------------------------------------------- +94 | identity[T8[E2]](1) // error + | ^ + | Found: (1 : Int) + | Required: Test.T8[Test.E2] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T8[Test.E2] + | failed since selector Test.E2 + | does not match case Test.E1 => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case Test.E2 => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:95:19 ----------------------------------------------------- +95 | identity[T8[E1]]("") // error + | ^^ + | Found: ("" : String) + | Required: Int + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:96:19 ----------------------------------------------------- +96 | identity[T8[E2]]("") // error + | ^^ + | Found: ("" : String) + | Required: Test.T8[Test.E2] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T8[Test.E2] + | failed since selector Test.E2 + | does not match case Test.E1 => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case Test.E2 => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:105:40 ---------------------------------------------------- +105 | identity[T9[Tuple2[Nothing, String]]](1) // error + | ^ + | Found: (1 : Int) + | Required: Test.T9[(Nothing, String)] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T9[(Nothing, String)] + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:106:40 ---------------------------------------------------- +106 | identity[T9[Tuple2[String, Nothing]]]("1") // error + | ^^^ + | Found: ("1" : String) + | Required: Test.T9[(String, Nothing)] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T9[(String, Nothing)] + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:107:37 ---------------------------------------------------- +107 | identity[T9[Tuple2[Int, Nothing]]](1) // error + | ^ + | Found: (1 : Int) + | Required: Test.T9[(Int, Nothing)] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T9[(Int, Nothing)] + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:108:37 ---------------------------------------------------- +108 | identity[T9[Tuple2[Nothing, Int]]]("1") // error + | ^^^ + | Found: ("1" : String) + | Required: Test.T9[(Nothing, Int)] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T9[(Nothing, Int)] + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:109:29 ---------------------------------------------------- +109 | identity[T9[Tuple2[_, _]]]("") // error + | ^^ + | Found: ("" : String) + | Required: Test.T9[(?, ?)] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T9[(?, ?)] + | failed since selector (?, ?) + | does not match case (Int, String) => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case (String, Int) => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:110:29 ---------------------------------------------------- +110 | identity[T9[Tuple2[_, _]]](1) // error + | ^ + | Found: (1 : Int) + | Required: Test.T9[(?, ?)] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T9[(?, ?)] + | failed since selector (?, ?) + | does not match case (Int, String) => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case (String, Int) => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:111:33 ---------------------------------------------------- +111 | identity[T9[Tuple2[Any, Any]]]("") // error + | ^^ + | Found: ("" : String) + | Required: Test.T9[(Any, Any)] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T9[(Any, Any)] + | failed since selector (Any, Any) + | does not match case (Int, String) => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case (String, Int) => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:112:33 ---------------------------------------------------- +112 | identity[T9[Tuple2[Any, Any]]](1) // error + | ^ + | Found: (1 : Int) + | Required: Test.T9[(Any, Any)] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.T9[(Any, Any)] + | failed since selector (Any, Any) + | does not match case (Int, String) => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case (String, Int) => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:122:39 ---------------------------------------------------- +122 | identity[TA[Box2[Int, Int, String]]](1) // error + | ^ + | Found: (1 : Int) + | Required: Test.TA[Test.Box2[Int, Int, String]] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.TA[Test.Box2[Int, Int, String]] + | failed since selector Test.Box2[Int, Int, String] + | does not match case Test.Box2[Int, Int, Int] => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case Test.Box2[Int, Int, String] => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:123:39 ---------------------------------------------------- +123 | identity[TA[Box2[Int, Int, String]]]("") // error + | ^^ + | Found: ("" : String) + | Required: Test.TA[Test.Box2[Int, Int, String]] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.TA[Test.Box2[Int, Int, String]] + | failed since selector Test.Box2[Int, Int, String] + | does not match case Test.Box2[Int, Int, Int] => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case Test.Box2[Int, Int, String] => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:144:41 ---------------------------------------------------- +144 | identity[TD[Box2_C[Int, Int, String]]]("") // error + | ^^ + | Found: ("" : String) + | Required: Test.TD[Test.Box2_C[Int, Int, String]] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test.TD[Test.Box2_C[Int, Int, String]] + | failed since selector Test.Box2_C[Int, Int, String] + | does not match case Test.Box2_C[Int, Int, Int] => Int + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case Test.Box2_C[Int, Int, String] => String + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:153:25 ---------------------------------------------------- +153 | def a[A]: M[Some[A]] = 1 // error + | ^ + | Found: (1 : Int) + | Required: Test2.M[Some[A]] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test2.M[Some[A]] + | failed since selector Some[A] + | does not match case Option[Int] => String + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case Some[_] => Int + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:154:25 ---------------------------------------------------- +154 | def b[A]: M[Some[A]] = "" // error + | ^^ + | Found: ("" : String) + | Required: Test2.M[Some[A]] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test2.M[Some[A]] + | failed since selector Some[A] + | does not match case Option[Int] => String + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case Some[_] => Int + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:168:23 ---------------------------------------------------- +168 | val a: M[Inv[A]] = 1 // error + | ^ + | Found: (1 : Int) + | Required: Test3.M[Test3.Inv[A]] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test3.M[Test3.Inv[A]] + | failed since selector Test3.Inv[A] + | does not match case Test3.Inv[Int] => String + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case _ => Int + +longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:187:25 ---------------------------------------------------- +187 | val a: M[Inv[A]] = 1 // error + | ^ + | Found: (1 : Int) + | Required: Test4.M[Test4.Inv[Foo.this.A]] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Test4.M[Test4.Inv[Foo.this.A]] + | failed since selector Test4.Inv[Foo.this.A] + | does not match case Test4.Inv[Int] => String + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case _ => Int + +longer explanation available when compiling with `-explain` From 16debbb667476fdd42a3a202d5a9f02a6246d06c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 11 Apr 2021 22:50:32 +0200 Subject: [PATCH 8/9] Add comments to MatchTypeTrace --- .../dotty/tools/dotc/core/MatchTypeTrace.scala | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala b/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala index 4ffff61042bf..054b445432a8 100644 --- a/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala +++ b/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala @@ -5,7 +5,10 @@ package core import Types._, Contexts._, Symbols._, Decorators._ import util.Property +/** A utility module to produce match type reduction traces in error messages. + */ object MatchTypeTrace: + private enum TraceEntry: case TryReduce(scrut: Type) case NoMatches(scrut: Type, cases: List[Type]) @@ -17,6 +20,9 @@ object MatchTypeTrace: private val MatchTrace = new Property.Key[MatchTrace] + /** Execute `op` and if it involves a failed match type reduction + * return the trace of that reduction. Otherwise return the empty string. + */ def record(op: Context ?=> Any)(using Context): String = val trace = new MatchTrace inContext(ctx.fresh.setProperty(MatchTrace, trace)) { @@ -30,6 +36,7 @@ object MatchTypeTrace: |${trace.entries.reverse.map(explainEntry)}%\n%""" } + /** Are we running an operation that records a match type trace? */ def isRecording(using Context): Boolean = ctx.property(MatchTrace).isDefined @@ -41,12 +48,22 @@ object MatchTypeTrace: case _ => case _ => + /** Record a failure that scrutinee `scrut` does not match any case in `cases`. + * Only the first failure is recorded. + */ def noMatches(scrut: Type, cases: List[Type])(using Context) = matchTypeFail(NoMatches(scrut, cases)) + /** Record a failure that scrutinee `scrut` does not match `stuckCase` but is + * not disjoint from it either, which means that the remaining cases `otherCases` + * cannot be visited. Only the first failure is recorded. + */ def stuck(scrut: Type, stuckCase: Type, otherCases: List[Type])(using Context) = matchTypeFail(Stuck(scrut, stuckCase, otherCases)) + /** Record in the trace that we are trying to reduce `scrut` when performing `op` + * If `op` succeeds the entry is removed after exit. If `op` fails, it stays. + */ def recurseWith(scrut: Type)(op: => Type)(using Context): Type = ctx.property(MatchTrace) match case Some(trace) => From f1cb0ba5e7f1aaf62fd853579a3cdb08599e3480 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 11 Apr 2021 23:21:18 +0200 Subject: [PATCH 9/9] Fix other tests Fix expectations so that we do not see package objects anymore. --- compiler/test-resources/repl/i5218 | 2 +- .../test/dotty/tools/languageserver/CompletionTest.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/test-resources/repl/i5218 b/compiler/test-resources/repl/i5218 index bdf8325b6962..143619bb1e1d 100644 --- a/compiler/test-resources/repl/i5218 +++ b/compiler/test-resources/repl/i5218 @@ -4,4 +4,4 @@ scala> 0.0 *: tuple val res0: (Double, Int, String, Long) = (0.0,1,2,3) scala> tuple ++ tuple val res1: Int *: String *: Long *: - scala.Tuple.Concat[scala.Tuple$package.EmptyTuple.type, tuple.type] = (1,2,3,1,2,3) + scala.Tuple.Concat[EmptyTuple.type, tuple.type] = (1,2,3,1,2,3) diff --git a/language-server/test/dotty/tools/languageserver/CompletionTest.scala b/language-server/test/dotty/tools/languageserver/CompletionTest.scala index ffb3f22d1ce1..6ac894800616 100644 --- a/language-server/test/dotty/tools/languageserver/CompletionTest.scala +++ b/language-server/test/dotty/tools/languageserver/CompletionTest.scala @@ -42,8 +42,8 @@ class CompletionTest { @Test def completionFromSyntheticPackageObject: Unit = { code"class Foo { val foo: IArr${m1} }".withSource - .completion(m1, Set(("IArray", Field, "scala.IArray"), - ("IArray", Module, "scala.IArray$package.IArray$"))) + .completion(m1, Set(("IArray", Module, "IArray$"), + ("IArray", Field, "scala.IArray"))) } @Test def completionFromJavaDefaults: Unit = {