Skip to content

Commit a7dfefb

Browse files
committed
Fix to interpolation
A capture set variable can appear several times at different variances in a type. So interpolating at the first variance encountered is wrong. Instead, we need to compute the overall variance and interpolate according to it.
1 parent 138bb32 commit a7dfefb

File tree

1 file changed

+44
-22
lines changed

1 file changed

+44
-22
lines changed

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

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -317,20 +317,42 @@ class CheckCaptures extends Recheck, SymTransformer:
317317
/** Instantiate capture set variables appearing contra-variantly to their
318318
* upper approximation.
319319
*/
320-
private def interpolator(sym: Symbol, startingVariance: Int = 1)(using Context) = new TypeTraverser:
321-
variance = startingVariance
322-
override def traverse(t: Type) = t match
323-
case t @ CapturingType(parent, refs) =>
324-
refs match
325-
case refs: CaptureSet.Var if !refs.isConst =>
326-
if variance < 0 then refs.solve()
327-
else refs.markSolved(provisional = !sym.isMutableVar)
328-
case _ =>
329-
traverse(parent)
330-
case t @ defn.RefinedFunctionOf(rinfo) =>
331-
traverse(rinfo)
332-
case _ =>
333-
traverseChildren(t)
320+
private def interpolate(tp: Type, sym: Symbol, startingVariance: Int = 1)(using Context): Unit =
321+
322+
object variances extends TypeTraverser:
323+
variance = startingVariance
324+
val varianceOfVar = EqHashMap[CaptureSet.Var, Int]()
325+
override def traverse(t: Type) = t match
326+
case t @ CapturingType(parent, refs) =>
327+
refs match
328+
case refs: CaptureSet.Var if !refs.isConst =>
329+
varianceOfVar(refs) = varianceOfVar.get(refs) match
330+
case Some(v0) => if v0 == 0 then 0 else (v0 + variance) / 2
331+
case None => variance
332+
case _ =>
333+
traverse(parent)
334+
case t @ defn.RefinedFunctionOf(rinfo) =>
335+
traverse(rinfo)
336+
case _ =>
337+
traverseChildren(t)
338+
339+
val interpolator = new TypeTraverser:
340+
override def traverse(t: Type) = t match
341+
case t @ CapturingType(parent, refs) =>
342+
refs match
343+
case refs: CaptureSet.Var if !refs.isConst =>
344+
if variances.varianceOfVar(refs) < 0 then refs.solve()
345+
else refs.markSolved(provisional = !sym.isMutableVar)
346+
case _ =>
347+
traverse(parent)
348+
case t @ defn.RefinedFunctionOf(rinfo) =>
349+
traverse(rinfo)
350+
case _ =>
351+
traverseChildren(t)
352+
353+
variances.traverse(tp)
354+
interpolator.traverse(tp)
355+
end interpolate
334356

335357
/* Also set any previously unset owners of toplevel Fresh instances to improve
336358
* error diagnostics in separation checking.
@@ -354,14 +376,14 @@ class CheckCaptures extends Recheck, SymTransformer:
354376
/** If `tpt` is an inferred type, interpolate capture set variables appearing contra-
355377
* variantly in it. Also anchor Fresh instances with anchorCaps.
356378
*/
357-
private def interpolateVarsIn(tpt: Tree, sym: Symbol)(using Context): Unit =
379+
private def interpolateIfInferred(tpt: Tree, sym: Symbol)(using Context): Unit =
358380
if tpt.isInstanceOf[InferredTypeTree] then
359-
interpolator(sym).traverse(tpt.nuType)
381+
interpolate(tpt.nuType, sym)
360382
.showing(i"solved vars for $sym in ${tpt.nuType}", capt)
361383
anchorCaps(sym).traverse(tpt.nuType)
362-
for msg <- ccState.approxWarnings do
363-
report.warning(msg, tpt.srcPos)
364-
ccState.approxWarnings.clear()
384+
for msg <- ccState.approxWarnings do
385+
report.warning(msg, tpt.srcPos)
386+
ccState.approxWarnings.clear()
365387

366388
/** Assert subcapturing `cs1 <: cs2` (available for debugging, otherwise unused) */
367389
def assertSub(cs1: CaptureSet, cs2: CaptureSet)(using Context) =
@@ -989,7 +1011,7 @@ class CheckCaptures extends Recheck, SymTransformer:
9891011
// for more info from the context, so we cannot interpolate. Note that we cannot
9901012
// expect to have all necessary info available at the point where the anonymous
9911013
// function is compiled since we do not propagate expected types into blocks.
992-
interpolateVarsIn(tree.tpt, sym)
1014+
interpolateIfInferred(tree.tpt, sym)
9931015

9941016
/** Recheck method definitions:
9951017
* - check body in a nested environment that tracks uses, in a nested level,
@@ -1035,7 +1057,7 @@ class CheckCaptures extends Recheck, SymTransformer:
10351057
if !sym.isAnonymousFunction then
10361058
// Anonymous functions propagate their type to the enclosing environment
10371059
// so it is not in general sound to interpolate their types.
1038-
interpolateVarsIn(tree.tpt, sym)
1060+
interpolateIfInferred(tree.tpt, sym)
10391061
curEnv = saved
10401062
end recheckDefDef
10411063

@@ -1781,7 +1803,7 @@ class CheckCaptures extends Recheck, SymTransformer:
17811803
inContext(ctx.fresh.setOwner(root)):
17821804
checkSelfAgainstParents(root, root.baseClasses)
17831805
val selfType = root.asClass.classInfo.selfType
1784-
interpolator(root, startingVariance = -1).traverse(selfType)
1806+
interpolate(selfType, root, startingVariance = -1)
17851807
selfType match
17861808
case CapturingType(_, refs: CaptureSet.Var)
17871809
if !root.isEffectivelySealed

0 commit comments

Comments
 (0)