From 5bafff7cc96f1f31f6e77620ca509dfa55d816b4 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 10 May 2023 16:39:12 +0100 Subject: [PATCH] Normalize match type usage during implicit lookup --- .../dotty/tools/dotc/core/TypeComparer.scala | 2 +- .../dotty/tools/dotc/core/TypeErrors.scala | 3 +++ .../dotty/tools/dotc/typer/Implicits.scala | 7 ++++++ tests/pos/i17395.scala | 25 +++++++++++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i17395.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 465978d329e6..ee8b8c3cdfcc 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -3180,7 +3180,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) { tp case Nil => val casesText = MatchTypeTrace.noMatchesText(scrut, cases) - throw TypeError(em"Match type reduction $casesText") + throw MatchTypeReductionError(em"Match type reduction $casesText") inFrozenConstraint { // Empty types break the basic assumption that if a scrutinee and a diff --git a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala index 24a207da6836..f59bd08da779 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala @@ -46,6 +46,9 @@ object TypeError: def toMessage(using Context) = msg end TypeError +class MatchTypeReductionError(msg: Message)(using Context) extends TypeError: + def toMessage(using Context) = msg + class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name])(using Context) extends TypeError: def toMessage(using Context) = em"malformed type: $pre is not a legal prefix for $denot because it contains abstract type member${if (absMembers.size == 1) "" else "s"} ${absMembers.mkString(", ")}" diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index a9631ad45e28..b5a20b6838bf 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -634,6 +634,13 @@ trait ImplicitRunInfo: case t: TypeLambda => for p <- t.paramRefs do partSeen += p traverseChildren(t) + case t: MatchType => + traverseChildren(t) + traverse(try t.normalized catch case _: MatchTypeReductionError => t) + case MatchType.InDisguise(mt) + if !t.isInstanceOf[LazyRef] // skip recursive applications (eg. Tuple.Map) + => + traverse(mt) case t => traverseChildren(t) diff --git a/tests/pos/i17395.scala b/tests/pos/i17395.scala new file mode 100644 index 000000000000..87c0a45a9ff5 --- /dev/null +++ b/tests/pos/i17395.scala @@ -0,0 +1,25 @@ +trait TC[T] + +object TC { + def optionTCForPart[T](implicit tc: TC[ExtractPart[T]]): TC[Option[ExtractPart[T]]] = new TC[Option[ExtractPart[T]]] {} +} + +type ExtractPart[T] = T match { + case PartField[t] => t +} +type PartField[T] = Any { type Part = T } + +class ValuePartHolder { + type Part = Value +} + +class Value +object Value { + implicit val tcValue: TC[Value] = new {} +} + +@main def main(): Unit = { +// import Value.tcValue // explicit import works around the issue, but shouldn't be necessary + val tc = TC.optionTCForPart[ValuePartHolder] + println(tc) +}