From f0cd5650853109d45475d558ef7adffe4fbd268b Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 4 Mar 2024 09:20:06 +0000 Subject: [PATCH] Rework MatchType recursion in collectParts This patch fixes a recursion situation in collectParts, by reimplementing a previous fix. Recursion is already attempted to be cutoff with `partSeen`, and `handleRecursion` is also used to prevent any unhandled recursion situations (like the one fixed here) from crashing the compiler. For context, AppliedType aren't hash-consed (i.e. the flyweight pattern) which means that every time you apply the same type arguments to the same type constructor you get a fresh AppliedType. Using i18171 as an example, the sequence of events where this matters is: 0. When typing BAZ, so much earlier than the collectParts call, because the MatchType on the rhs of BAZ reduces at definition site, the RHS is reduced to the `DFVal[BAZREC[T]]`, which means that BAZ's info is a TypeAlias rather than a MatchAlias, meaning it can dealias. 1. `BAZREC[Any]` is extracted by MatchType.InDisguise, which applies the Any to return a fresh MatchType 2. `Tuple.Map[Any, BAZ]` is also extracted by MatchType.InDisguise, which returns its own fresh MatchType 3. `BAZ[h]` dealiases to a fresh `DFVal[BAZREC[h]]` instance (see step 0) 4. `BAZREC[h]` is extracted by MatchType.InDisguise, repeating the cycle The reason that the cases with MatchType.InDisguise and MatchType were introduced is i17395. Looking back, it seems the only need is to capture any parts that are in the reduction of an applied MatchType. With this patch applied in the case of i18171, this now cuts off quickly, as `BAZREC[Any]` doesn't reduce to anything. In the case of i17395, `ExtractPart[ValuePartHolder]` reduces to `Value`, so `Value` is successfully recorded as a part. --- .../dotty/tools/dotc/typer/Implicits.scala | 8 +------ tests/pos/i17395-spec.ordered.scala | 17 ++++++++++++++ tests/pos/i19857.scala | 23 +++++++++++++++++++ 3 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 tests/pos/i17395-spec.ordered.scala create mode 100644 tests/pos/i19857.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 0a26ea697a6a..5162b3fed1b9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -661,15 +661,9 @@ trait ImplicitRunInfo: case t: TypeLambda => for p <- t.paramRefs do partSeen += p traverseChildren(t) - case t: MatchType => - traverseChildren(t) - traverse(t.normalized) - case MatchType.InDisguise(mt) - if !t.isInstanceOf[LazyRef] // skip recursive applications (eg. Tuple.Map) - => - traverse(mt) case t => traverseChildren(t) + traverse(t.normalized) catch case ex: Throwable => handleRecursive("collectParts of", t.show, ex) def apply(tp: Type): collection.Set[Type] = diff --git a/tests/pos/i17395-spec.ordered.scala b/tests/pos/i17395-spec.ordered.scala new file mode 100644 index 000000000000..6e6ae8dd1e59 --- /dev/null +++ b/tests/pos/i17395-spec.ordered.scala @@ -0,0 +1,17 @@ +trait ThingWithPart { type Part } +type PartField[A] = ThingWithPart { type Part = A } +type ExtractPart[B] = B match { case PartField[a] => a } + +trait TC[C] +object TC: + def tcForOptionPart[D](implicit tc: TC[ExtractPart[D]]): TC[Option[ExtractPart[D]]] = new {} + +class Value +object Value: + implicit val tcValue: TC[Value] = new {} + +class ValuePartHolder extends ThingWithPart { type Part = Value } + +class Test: + def t1: Unit = + val tc = TC.tcForOptionPart[ValuePartHolder] diff --git a/tests/pos/i19857.scala b/tests/pos/i19857.scala new file mode 100644 index 000000000000..aeb6e49111c6 --- /dev/null +++ b/tests/pos/i19857.scala @@ -0,0 +1,23 @@ +sealed trait DFTypeAny + +sealed trait DFTuple[T <: NonEmptyTuple] extends DFTypeAny + +sealed trait DFBit extends DFTypeAny + +sealed trait DFValOf[T] + +type Of[T] <: DFTypeAny = T match + case DFTypeAny => T & DFTypeAny + case Product => FromProduct[T] + +type JUSTVAL[T] = DFValOf[Of[T]] + +type FromProduct[T <: Product] <: DFTypeAny = T match + case NonEmptyTuple => DFTuple[Tuple.Map[T, JUSTVAL]] + +trait Width2[T] + +object Width2: + inline given [T]: Width2[T] = new Width2[T] {} + +val x = summon[Width2[Of[(DFBit, DFBit)]]]