Skip to content

Commit 20d95b0

Browse files
dwijnandEugeneFlesselle
authored andcommitted
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.
1 parent 09f38cf commit 20d95b0

File tree

3 files changed

+41
-7
lines changed

3 files changed

+41
-7
lines changed

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

+1-7
Original file line numberDiff line numberDiff line change
@@ -661,15 +661,9 @@ trait ImplicitRunInfo:
661661
case t: TypeLambda =>
662662
for p <- t.paramRefs do partSeen += p
663663
traverseChildren(t)
664-
case t: MatchType =>
665-
traverseChildren(t)
666-
traverse(t.normalized)
667-
case MatchType.InDisguise(mt)
668-
if !t.isInstanceOf[LazyRef] // skip recursive applications (eg. Tuple.Map)
669-
=>
670-
traverse(mt)
671664
case t =>
672665
traverseChildren(t)
666+
traverse(t.normalized)
673667
catch case ex: Throwable => handleRecursive("collectParts of", t.show, ex)
674668

675669
def apply(tp: Type): collection.Set[Type] =

tests/pos/i17395-spec.ordered.scala

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
trait ThingWithPart { type Part }
2+
type PartField[A] = ThingWithPart { type Part = A }
3+
type ExtractPart[B] = B match { case PartField[a] => a }
4+
5+
trait TC[C]
6+
object TC:
7+
def tcForOptionPart[D](implicit tc: TC[ExtractPart[D]]): TC[Option[ExtractPart[D]]] = new {}
8+
9+
class Value
10+
object Value:
11+
implicit val tcValue: TC[Value] = new {}
12+
13+
class ValuePartHolder extends ThingWithPart { type Part = Value }
14+
15+
class Test:
16+
def t1: Unit =
17+
val tc = TC.tcForOptionPart[ValuePartHolder]

tests/pos/i19857.scala

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
sealed trait DFTypeAny
2+
3+
sealed trait DFTuple[T <: NonEmptyTuple] extends DFTypeAny
4+
5+
sealed trait DFBit extends DFTypeAny
6+
7+
sealed trait DFValOf[T]
8+
9+
type Of[T] <: DFTypeAny = T match
10+
case DFTypeAny => T & DFTypeAny
11+
case Product => FromProduct[T]
12+
13+
type JUSTVAL[T] = DFValOf[Of[T]]
14+
15+
type FromProduct[T <: Product] <: DFTypeAny = T match
16+
case NonEmptyTuple => DFTuple[Tuple.Map[T, JUSTVAL]]
17+
18+
trait Width2[T]
19+
20+
object Width2:
21+
inline given [T]: Width2[T] = new Width2[T] {}
22+
23+
val x = summon[Width2[Of[(DFBit, DFBit)]]]

0 commit comments

Comments
 (0)