Skip to content

Commit 5036105

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 551eae4 commit 5036105

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
@@ -5009,6 +5009,8 @@ object Types extends TypeUtils {
50095009
case ex: Throwable =>
50105010
handleRecursive("normalizing", s"${scrutinee.show} match ..." , ex)
50115011

5012+
private def thisMatchType = this
5013+
50125014
def reduced(using Context): Type = {
50135015

50145016
def contextInfo(tp: Type): Type = tp match {
@@ -5027,12 +5029,44 @@ object Types extends TypeUtils {
50275029
reductionContext = util.HashMap()
50285030
for (tp <- footprint)
50295031
reductionContext(tp) = contextInfo(tp)
5030-
typr.println(i"footprint for $this $hashCode: ${footprint.toList.map(x => (x, contextInfo(x)))}%, %")
5032+
matchTypes.println(i"footprint for $this $hashCode: ${footprint.toList.map(x => (x, contextInfo(x)))}%, %")
50315033

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

50375071
record("MatchType.reduce called")
50385072
if !Config.cacheMatchReduced
@@ -5044,19 +5078,21 @@ object Types extends TypeUtils {
50445078
if (myReduced != null) record("MatchType.reduce cache miss")
50455079
myReduced =
50465080
trace(i"reduce match type $this $hashCode", matchTypes, show = true)(withMode(Mode.Type) {
5081+
computeFootprint()
50475082
def matchCases(cmp: TrackingTypeComparer): Type =
50485083
val saved = ctx.typerState.snapshot()
50495084
try cmp.matchCases(scrutinee.normalized, cases.map(MatchTypeCaseSpec.analyze(_)))
50505085
catch case ex: Throwable =>
50515086
handleRecursive("reduce type ", i"$scrutinee match ...", ex)
50525087
finally
5053-
updateReductionContext(cmp.footprint)
5088+
//updateReductionContext(cmp.footprint)
50545089
ctx.typerState.resetTo(saved)
50555090
// this drops caseLambdas in constraint and undoes any typevar
50565091
// instantiations during matchtype reduction
50575092

50585093
TypeComparer.tracked(matchCases)
50595094
})
5095+
//else println(i"no change for $this $hashCode / $myReduced")
50605096
myReduced.nn
50615097
}
50625098

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)