Skip to content

Commit 3e5a712

Browse files
committed
Improve closure typing
If the closure has an expected function type with a fully defined result type, take the internalized result type as the local return type of the closure. This has the effect that some conformance tests are now done with Fresh instead Result caps. This means a now can widen a local reference to a result cap, since the comparison is done between the local reference and the internalized FreshCap. Previously this failed since we compared a local cap with result cap, and result caps only subtype other result caps. It also propagates types more aggressively into closure bodies, which sometimes reduces the error span and improves the error message.
1 parent 45ad465 commit 3e5a712

File tree

1 file changed

+63
-0
lines changed

1 file changed

+63
-0
lines changed

compiler/src/dotty/tools/dotc/cc/Capability.scala

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,69 @@ object Capabilities:
10141014
def inverse = Inverse()
10151015
end Internalize
10161016

1017+
/** The local dual of a result type of a closure type.
1018+
* @param binder the method type of the anonymous function whose result is mapped
1019+
* @pre the context's owner is the anonymous function
1020+
*/
1021+
class Internalize(binder: MethodType)(using Context) extends BiTypeMap:
1022+
thisMap =>
1023+
1024+
val sym = ctx.owner
1025+
assert(sym.isAnonymousFunction)
1026+
val paramSyms = atPhase(ctx.phase.prev):
1027+
// We need to ask one phase before since `sym` should not be completed as a side effect.
1028+
// The result of Internalize is used to se the result type of an anonymous function, and
1029+
// the new info of that function is built with the result.
1030+
sym.paramSymss.head
1031+
val resultToFresh = EqHashMap[ResultCap, FreshCap]()
1032+
val freshToResult = EqHashMap[FreshCap, ResultCap]()
1033+
1034+
override def apply(t: Type) =
1035+
if variance < 0 then t
1036+
else t match
1037+
case t: ParamRef =>
1038+
if t.binder == this.binder then paramSyms(t.paramNum).termRef else t
1039+
case _ => mapOver(t)
1040+
1041+
override def mapCapability(c: Capability, deep: Boolean): Capability = c match
1042+
case r: ResultCap if r.binder == this.binder =>
1043+
resultToFresh.get(r) match
1044+
case Some(f) => f
1045+
case None =>
1046+
val f = FreshCap(Origin.LocalInstance(binder.resType))
1047+
resultToFresh(r) = f
1048+
freshToResult(f) = r
1049+
f
1050+
case _ =>
1051+
super.mapCapability(c, deep)
1052+
1053+
class Inverse extends BiTypeMap:
1054+
def apply(t: Type): Type =
1055+
if variance < 0 then t
1056+
else t match
1057+
case t: TermRef if paramSyms.contains(t) =>
1058+
binder.paramRefs(paramSyms.indexOf(t.symbol))
1059+
case _ => mapOver(t)
1060+
1061+
override def mapCapability(c: Capability, deep: Boolean): Capability = c match
1062+
case f: FreshCap if f.owner == sym =>
1063+
freshToResult.get(f) match
1064+
case Some(r) => r
1065+
case None =>
1066+
val r = ResultCap(binder)
1067+
resultToFresh(r) = f
1068+
freshToResult(f) = r
1069+
r
1070+
case _ => super.mapCapability(c, deep)
1071+
1072+
def inverse = thisMap
1073+
override def toString = thisMap.toString + ".inverse"
1074+
end Inverse
1075+
1076+
override def toString = "InternalizeClosureResult"
1077+
def inverse = Inverse()
1078+
end Internalize
1079+
10171080
/** Map top-level free existential variables one-to-one to Fresh instances */
10181081
def resultToFresh(tp: Type, origin: Origin)(using Context): Type =
10191082
val subst = new TypeMap:

0 commit comments

Comments
 (0)