Skip to content

Commit 2366886

Browse files
committed
Avoid multiple maps when creating symbol infos
Use a single BiTypeMap to map from inferred result and parameters to method info. This improves efficiency and debuggability by reducing the frequence of multiple stacked maps capture sets.
1 parent e7507a3 commit 2366886

File tree

1 file changed

+49
-30
lines changed

1 file changed

+49
-30
lines changed

compiler/src/dotty/tools/dotc/transform/Recheck.scala

Lines changed: 49 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -80,25 +80,34 @@ abstract class Recheck extends Phase, IdentityDenotTransformer:
8080

8181
object transformTypes extends TreeTraverser:
8282

83-
class SubstParams(from: List[Symbol], to: MethodOrPoly)(using Context)
83+
// Substitute parameter symbols in `from` to paramRefs in corresponding
84+
// method or poly types `to`. We use a single BiTypeMap to do everything.
85+
class SubstParams(from: List[List[Symbol]], to: List[LambdaType])(using Context)
8486
extends DeepTypeMap, BiTypeMap:
85-
private val paramRefs = to.paramRefs
86-
private val fromRefs = from.map(_.namedType)
8787

8888
def apply(t: Type): Type = t match
8989
case t: NamedType =>
9090
val sym = t.symbol
91-
def recur(from: List[Symbol], to: List[Type]): Type =
92-
if from.isEmpty then t
93-
else if sym eq from.head then to.head
94-
else recur(from.tail, to.tail)
95-
recur(from, paramRefs)
91+
def outer(froms: List[List[Symbol]], tos: List[LambdaType]): Type =
92+
def inner(from: List[Symbol], to: List[ParamRef]): Type =
93+
if from.isEmpty then outer(froms.tail, tos.tail)
94+
else if sym eq from.head then to.head
95+
else inner(from.tail, to.tail)
96+
if tos.isEmpty then t
97+
else inner(froms.head, tos.head.paramRefs)
98+
outer(from, to)
9699
case _ =>
97100
mapOver(t)
98101

99102
def inverse(t: Type): Type = t match
100-
case t: ParamRef if t.binder eq to => fromRefs(t.paramNum)
101-
case _ => mapOver(t)
103+
case t: ParamRef =>
104+
def recur(from: List[LambdaType], to: List[List[Symbol]]): Type =
105+
if from.isEmpty then t
106+
else if t.binder eq from.head then to.head(t.paramNum).namedType
107+
else recur(from.tail, to.tail)
108+
recur(to, from)
109+
case _ =>
110+
mapOver(t)
102111
end SubstParams
103112

104113
def traverse(tree: Tree)(using Context) =
@@ -108,27 +117,37 @@ abstract class Recheck extends Phase, IdentityDenotTransformer:
108117
transformType(tree.tpe, tree.isInstanceOf[InferredTypeTree]).rememberFor(tree)
109118
case tree: ValOrDefDef =>
110119
val sym = tree.symbol
111-
def integrateRT(restp: Type, info: Type, psymss: List[List[Symbol]], pinfoss: List[List[Type]]): Type = info match
112-
case mt: MethodOrPoly =>
113-
val psyms = psymss.head
114-
val subst = SubstParams(psyms, mt)
115-
mt.derivedLambdaType(
116-
paramInfos =
117-
if psyms.exists(isUpdated) then pinfoss.head.asInstanceOf[List[mt.PInfo]]
118-
else mt.paramInfos,
119-
resType = integrateRT(
120-
subst(restp), mt.resType, psymss.tail, pinfoss.tail.nestedMap(subst)))
121-
case info: ExprType =>
122-
info.derivedExprType(resType = restp)
123-
case _ =>
124-
restp
120+
121+
// replace an existing symbol info with inferred types
122+
def integrateRT(
123+
info: Type, // symbol info to replace
124+
psymss: List[List[Symbol]], // the local (type and trem) parameter symbols corresponding to `info`
125+
prevPsymss: List[List[Symbol]], // the local parameter symbols seen previously in reverse order
126+
prevLambdas: List[LambdaType] // the outer method and polytypes generated previously in reverse order
127+
): Type =
128+
info match
129+
case mt: MethodOrPoly =>
130+
val psyms = psymss.head
131+
mt.companion(mt.paramNames)(
132+
mt1 =>
133+
if !psyms.exists(isUpdated) && !mt.isParamDependent && prevLambdas.isEmpty then
134+
mt.paramInfos
135+
else
136+
val subst = SubstParams(psyms :: prevPsymss, mt1 :: prevLambdas)
137+
psyms.map(psym => subst(psym.info).asInstanceOf[mt.PInfo]),
138+
mt1 =>
139+
integrateRT(mt.resType, psymss.tail, psyms :: prevPsymss, mt1 :: prevLambdas)
140+
)
141+
case info: ExprType =>
142+
info.derivedExprType(resType =
143+
integrateRT(info.resType, psymss, prevPsymss, prevLambdas))
144+
case _ =>
145+
val restp = knownType(tree.tpt)
146+
if prevLambdas.isEmpty then restp
147+
else SubstParams(prevPsymss, prevLambdas)(restp)
148+
125149
if tree.tpt.hasAttachment(RecheckedType) && !sym.isConstructor then
126-
val newInfo = integrateRT(
127-
knownType(tree.tpt),
128-
sym.info,
129-
sym.paramSymss,
130-
sym.paramSymss.nestedMap(_.info)
131-
)
150+
val newInfo = integrateRT(sym.info, sym.paramSymss, Nil, Nil)
132151
.showing(i"update info $sym: ${sym.info} --> $result", recheckr)
133152
if newInfo ne sym.info then
134153
val completer = new LazyType:

0 commit comments

Comments
 (0)