Skip to content

Commit b31a247

Browse files
committed
Interpolate type variables when symbols are completed
1 parent 85bd587 commit b31a247

File tree

5 files changed

+53
-8
lines changed

5 files changed

+53
-8
lines changed

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

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,15 @@ sealed abstract class CaptureSet extends Showable:
173173
def substParams(tl: BindingType, to: List[Type])(using Context) =
174174
map(Substituters.SubstParamsMap(tl, to))
175175

176+
/** An upper approximation of this capture set. This is the set itself
177+
* except for real (non-mapped, non-filtered) capture set variables, where
178+
* it is the intersection of all upper approximations of known supersets
179+
* of the variable.
180+
* The upper approximation is meaningful only if it is constant. If not,
181+
* `upperApprox` can return an arbitrary capture set variable.
182+
*/
183+
def upperApprox(using Context): CaptureSet
184+
176185
def toRetainsTypeArg(using Context): Type =
177186
assert(isConst)
178187
((NoType: Type) /: elems) ((tp, ref) =>
@@ -221,6 +230,8 @@ object CaptureSet:
221230

222231
def addSuper(cs: CaptureSet)(using Context, VarState) = CompareResult.OK
223232

233+
def upperApprox(using Context): CaptureSet = this
234+
224235
override def toString = elems.toString
225236
end Const
226237

@@ -265,14 +276,31 @@ object CaptureSet:
265276
CompareResult.fail(this)
266277

267278
def addSuper(cs: CaptureSet)(using Context, VarState): CompareResult =
268-
if (cs eq this) || cs.elems.contains(defn.captureRoot.termRef) then
279+
if (cs eq this) || cs.elems.contains(defn.captureRoot.termRef) || isConst then
269280
CompareResult.OK
270281
else if recordDepsState() then
271282
deps += cs
272283
CompareResult.OK
273284
else
274285
CompareResult.fail(this)
275286

287+
def upperApprox(using Context): CaptureSet =
288+
if isConst then this
289+
else (universal /: deps) { (acc, sup) =>
290+
if acc.isConst then
291+
val supApprox = sup.upperApprox
292+
if supApprox.isConst then acc ** supApprox else supApprox
293+
else acc
294+
}
295+
296+
def solve(variance: Int)(using Context): Unit =
297+
if variance > 0 then isSolved = true
298+
else if variance < 0 then
299+
val approx = upperApprox
300+
if approx.isConst then
301+
elems = approx.elems
302+
isSolved = true
303+
276304
override def toString = s"Var$id$elems"
277305
end Var
278306

@@ -307,6 +335,8 @@ object CaptureSet:
307335
else CompareResult.fail(this)
308336
else result
309337

338+
override def upperApprox(using Context): CaptureSet = this
339+
310340
override def toString = s"Mapped$id($cv, elems = $elems)"
311341
end Mapped
312342

@@ -323,6 +353,8 @@ object CaptureSet:
323353
.showing(i"propagating new elems $newElems backward from $this to $cv", capt)
324354
else r
325355

356+
override def upperApprox(using Context): CaptureSet = this
357+
326358
override def toString = s"BiMapped$id($cv, elems = $elems)"
327359
end BiMapped
328360

@@ -334,6 +366,8 @@ object CaptureSet:
334366
override def addNewElems(newElems: Refs, origin: CaptureSet)(using Context, VarState): CompareResult =
335367
super.addNewElems(newElems.filter(p), origin)
336368

369+
override def upperApprox(using Context): CaptureSet = this
370+
337371
override def toString = s"${getClass.getSimpleName}$id($cv, elems = $elems)"
338372
end Filtered
339373

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,14 @@ class CheckCaptures extends Recheck:
203203
tp
204204
end transformType
205205

206+
def interpolateVars(using Context) = new TypeTraverser:
207+
override def traverse(t: Type) =
208+
t match
209+
case CapturingType(_, refs: CaptureSet.Var, _) if !refs.isConst =>
210+
refs.solve(variance)
211+
case _ =>
212+
traverseChildren(t)
213+
206214
private var curEnv: Env = Env(NoSymbol, CaptureSet.empty, false, null)
207215

208216
private val myCapturedVars: util.EqHashMap[Symbol, CaptureSet] = EqHashMap()
@@ -268,14 +276,17 @@ class CheckCaptures extends Recheck:
268276
super.recheckIdent(tree)
269277

270278
override def recheckValDef(tree: ValDef, sym: Symbol)(using Context): Unit =
271-
super.recheckValDef(tree, sym)
279+
try super.recheckValDef(tree, sym)
280+
finally interpolateVars.traverse(sym.info)
272281

273282
override def recheckDefDef(tree: DefDef, sym: Symbol)(using Context): Unit =
274283
val saved = curEnv
275284
val localSet = capturedVars(sym)
276285
if !localSet.isAlwaysEmpty then curEnv = Env(sym, localSet, false, curEnv)
277286
try super.recheckDefDef(tree, sym)
278-
finally curEnv = saved
287+
finally
288+
interpolateVars.traverse(sym.info)
289+
curEnv = saved
279290

280291
override def recheckClassDef(tree: TypeDef, impl: Template, cls: ClassSymbol)(using Context): Type =
281292
for param <- cls.paramGetters do
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/boxmap.scala:14:2 ----------------------------------------
22
14 | () => b[Box[B]]((x: A) => box(f(x))) // error
33
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4-
| Found: {f} () => ? Box[B]
4+
| Found: {f} () => Box[B]
55
| Required: () => Box[B]
66

77
longer explanation available when compiling with `-explain`

tests/neg-custom-args/captures/capt1.check

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:3:2 ------------------------------------------
22
3 | () => if x == null then y else y // error
33
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4-
| Found: {x} () => ? C
4+
| Found: {x} () => C
55
| Required: () => C
66

77
longer explanation available when compiling with `-explain`
88
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:6:2 ------------------------------------------
99
6 | () => if x == null then y else y // error
1010
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
11-
| Found: {x} () => ? C
11+
| Found: {x} () => C
1212
| Required: Matchable
1313

1414
longer explanation available when compiling with `-explain`
@@ -40,7 +40,7 @@ longer explanation available when compiling with `-explain`
4040
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:31:24 ----------------------------------------
4141
31 | val z2 = h[() => Cap](() => x)(() => C()) // error
4242
| ^^^^^^^
43-
| Found: {x} () => ? Cap
43+
| Found: {x} () => Cap
4444
| Required: () => Cap
4545

4646
longer explanation available when compiling with `-explain`

tests/neg-custom-args/captures/try.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try.scala:28:43 ------------------------------------------
22
28 | val b = handle[Exception, () => Nothing] { // error
33
| ^
4-
| Found: ? (x: CanThrow[Exception]) => {x} () => ? Nothing
4+
| Found: ? (x: CanThrow[Exception]) => {x} () => Nothing
55
| Required: CanThrow[Exception] => () => Nothing
66
29 | (x: CanThrow[Exception]) => () => raise(new Exception)(using x)
77
30 | } {

0 commit comments

Comments
 (0)