Skip to content

Commit f67c889

Browse files
committed
New footprint calculation scheme
The old one clearly did not work. It either never worked or was disabled by the changes to matchtype reduction. I now changed it to a more straightforward scheme that computes the footprint directly instead of relying on TypeComparer to produce the right trace.
1 parent f15bbda commit f67c889

File tree

2 files changed

+139
-4
lines changed

2 files changed

+139
-4
lines changed

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

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5003,6 +5003,8 @@ object Types extends TypeUtils {
50035003
case ex: Throwable =>
50045004
handleRecursive("normalizing", s"${scrutinee.show} match ..." , ex)
50055005

5006+
private def thisMatchType = this
5007+
50065008
def reduced(using Context): Type = {
50075009

50085010
def contextInfo(tp: Type): Type = tp match {
@@ -5021,12 +5023,44 @@ object Types extends TypeUtils {
50215023
reductionContext = util.HashMap()
50225024
for (tp <- footprint)
50235025
reductionContext(tp) = contextInfo(tp)
5024-
typr.println(i"footprint for $this $hashCode: ${footprint.toList.map(x => (x, contextInfo(x)))}%, %")
5026+
matchTypes.println(i"footprint for $this $hashCode: ${footprint.toList.map(x => (x, contextInfo(x)))}%, %")
50255027

50265028
def isUpToDate: Boolean =
5027-
reductionContext.keysIterator.forall { tp =>
5029+
reductionContext.keysIterator.forall: tp =>
50285030
reductionContext(tp) `eq` contextInfo(tp)
5029-
}
5031+
5032+
def computeFootprint(): Unit =
5033+
new TypeTraverser:
5034+
var footprint: Set[Type] = Set()
5035+
var deep: Boolean = true
5036+
val seen = util.HashSet[Type]()
5037+
def traverse(tp: Type) =
5038+
if !seen.contains(tp) then
5039+
seen += tp
5040+
tp match
5041+
case tp: NamedType =>
5042+
if tp.symbol.is(TypeParam) then footprint += tp
5043+
traverseChildren(tp)
5044+
case _: AppliedType | _: RefinedType =>
5045+
if deep then traverseChildren(tp)
5046+
case TypeBounds(lo, hi) =>
5047+
traverse(hi)
5048+
case tp: TypeVar =>
5049+
footprint += tp
5050+
traverse(tp.underlying)
5051+
case tp: TypeParamRef =>
5052+
footprint += tp
5053+
case _ =>
5054+
traverseChildren(tp)
5055+
end traverse
5056+
5057+
traverse(scrutinee)
5058+
deep = false
5059+
cases.foreach(traverse)
5060+
reductionContext = util.HashMap()
5061+
for tp <- footprint do
5062+
reductionContext(tp) = contextInfo(tp)
5063+
matchTypes.println(i"footprint for $thisMatchType $hashCode: ${footprint.toList.map(x => (x, contextInfo(x)))}%, %")
50305064

50315065
record("MatchType.reduce called")
50325066
if !Config.cacheMatchReduced
@@ -5038,19 +5072,21 @@ object Types extends TypeUtils {
50385072
if (myReduced != null) record("MatchType.reduce cache miss")
50395073
myReduced =
50405074
trace(i"reduce match type $this $hashCode", matchTypes, show = true)(inMode(Mode.Type) {
5075+
computeFootprint()
50415076
def matchCases(cmp: TrackingTypeComparer): Type =
50425077
val saved = ctx.typerState.snapshot()
50435078
try cmp.matchCases(scrutinee.normalized, cases.map(MatchTypeCaseSpec.analyze(_)))
50445079
catch case ex: Throwable =>
50455080
handleRecursive("reduce type ", i"$scrutinee match ...", ex)
50465081
finally
5047-
updateReductionContext(cmp.footprint)
5082+
//updateReductionContext(cmp.footprint)
50485083
ctx.typerState.resetTo(saved)
50495084
// this drops caseLambdas in constraint and undoes any typevar
50505085
// instantiations during matchtype reduction
50515086

50525087
TypeComparer.tracked(matchCases)
50535088
})
5089+
//else println(i"no change for $this $hashCode / $myReduced")
50545090
myReduced.nn
50555091
}
50565092

tests/pos/bad-footprint.scala

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
2+
object NamedTuple:
3+
4+
opaque type AnyNamedTuple = Any
5+
opaque type NamedTuple[N <: Tuple, +V <: Tuple] >: V <: AnyNamedTuple = V
6+
7+
export NamedTupleDecomposition.{Names, DropNames}
8+
9+
/** The type of the named tuple `X` mapped with the type-level function `F`.
10+
* If `X = (n1 : T1, ..., ni : Ti)` then `Map[X, F] = `(n1 : F[T1], ..., ni : F[Ti])`.
11+
*/
12+
type Map[X <: AnyNamedTuple, F[_ <: Tuple.Union[DropNames[X]]]] =
13+
NamedTuple[Names[X], Tuple.Map[DropNames[X], F]]
14+
15+
end NamedTuple
16+
17+
object NamedTupleDecomposition:
18+
import NamedTuple.*
19+
20+
/** The names of a named tuple, represented as a tuple of literal string values. */
21+
type Names[X <: AnyNamedTuple] <: Tuple = X match
22+
case NamedTuple[n, _] => n
23+
24+
/** The value types of a named tuple represented as a regular tuple. */
25+
type DropNames[NT <: AnyNamedTuple] <: Tuple = NT match
26+
case NamedTuple[_, x] => x
27+
end NamedTupleDecomposition
28+
29+
class Expr[Result]
30+
31+
object Expr:
32+
import NamedTuple.{NamedTuple, AnyNamedTuple}
33+
34+
type Of[A] = Expr[A]
35+
36+
type StripExpr[E] = E match
37+
case Expr.Of[b] => b
38+
39+
case class Ref[A]($name: String = "") extends Expr.Of[A]
40+
41+
case class Join[A <: AnyNamedTuple](a: A)
42+
extends Expr.Of[NamedTuple.Map[A, StripExpr]]
43+
end Expr
44+
45+
trait Query[A]
46+
47+
object Query:
48+
// Extension methods to support for-expression syntax for queries
49+
extension [R](x: Query[R])
50+
def map[B](f: Expr.Ref[R] => Expr.Of[B]): Query[B] = ???
51+
52+
case class City(zipCode: Int, name: String, population: Int)
53+
54+
object Test:
55+
import Expr.StripExpr
56+
import NamedTuple.{NamedTuple, AnyNamedTuple}
57+
58+
val cities: Query[City] = ???
59+
val q6 =
60+
cities.map: city =>
61+
val x: NamedTuple[
62+
("name", "zipCode"),
63+
(Expr.Of[String], Expr.Of[Int])] = ???
64+
Expr.Join(x)
65+
66+
/* Was error:
67+
68+
-- [E007] Type Mismatch Error: bad-footprint.scala:60:16 -----------------------
69+
60 | cities.map: city =>
70+
| ^
71+
|Found: Expr.Ref[City] =>
72+
| Expr[
73+
| NamedTuple.NamedTuple[(("name" : String), ("zipCode" : String)), (String,
74+
| Int)]
75+
| ]
76+
|Required: Expr.Ref[City] =>
77+
| Expr[
78+
| NamedTuple.NamedTuple[
79+
| NamedTupleDecomposition.Names[
80+
| NamedTuple.NamedTuple[(("name" : String), ("zipCode" : String)), (
81+
| Expr[String], Expr[Int])]
82+
| ],
83+
| Tuple.Map[
84+
| NamedTupleDecomposition.DropNames[
85+
| NamedTuple.NamedTuple[(("name" : String), ("zipCode" : String)), (
86+
| Expr[String], Expr[Int])]
87+
| ],
88+
| Expr.StripExpr]
89+
| ]
90+
| ]
91+
61 | val x: NamedTuple[
92+
62 | ("name", "zipCode"),
93+
63 | (Expr.Of[String], Expr.Of[Int])] = ???
94+
64 | Expr.Join(x)
95+
|
96+
| longer explanation available when compiling with `-explain`
97+
1 error found
98+
99+
*/

0 commit comments

Comments
 (0)