@@ -5,7 +5,7 @@ package cc
5
5
import core .*
6
6
import Phases .* , DenotTransformers .* , SymDenotations .*
7
7
import Contexts .* , Names .* , Flags .* , Symbols .* , Decorators .*
8
- import Types .* , StdNames .*
8
+ import Types .* , StdNames .* , Denotations . *
9
9
import config .Printers .{capt , recheckr }
10
10
import config .Config
11
11
import ast .{tpd , untpd , Trees }
@@ -89,7 +89,7 @@ object CheckCaptures:
89
89
elem.tpe match
90
90
case ref : CaptureRef =>
91
91
if ! ref.canBeTracked then
92
- report.error(em " $elem cannot be tracked since it is not a parameter or a local variable " , elem.srcPos)
92
+ report.error(em " $elem cannot be tracked since it is not a parameter or local value " , elem.srcPos)
93
93
case tpe =>
94
94
report.error(em " $elem: $tpe is not a legal element of a capture set " , elem.srcPos)
95
95
@@ -288,16 +288,34 @@ class CheckCaptures extends Recheck, SymTransformer:
288
288
* outcome of a `mightSubcapture` test. It picks `{f}` if this might subcapture Cr
289
289
* and Cr otherwise.
290
290
*/
291
- override def recheckSelection (tree : Select , qualType : Type , name : Name )(using Context ) = {
292
- val selType = super .recheckSelection(tree, qualType, name)
291
+ override def recheckSelection (tree : Select , qualType : Type , name : Name , pt : Type )(using Context ) = {
292
+ def disambiguate (denot : Denotation ): Denotation = denot match
293
+ case MultiDenotation (denot1, denot2) =>
294
+ // This case can arise when we try to merge multiple types that have different
295
+ // capture sets on some part. For instance an asSeenFrom might produce
296
+ // a bi-mapped capture set arising from a substition. Applying the same substitution
297
+ // to the same type twice will nevertheless produce different capture setsw which can
298
+ // lead to a failure in disambiguation since neither alternative is better than the
299
+ // other in a frozen constraint. An example test case is disambiguate-select.scala.
300
+ // We address the problem by disambiguating while ignoring all capture sets as a fallback.
301
+ withMode(Mode .IgnoreCaptures ) {
302
+ disambiguate(denot1).meet(disambiguate(denot2), qualType)
303
+ }
304
+ case _ => denot
305
+
306
+ val selType = recheckSelection(tree, qualType, name, disambiguate)
293
307
val selCs = selType.widen.captureSet
294
308
if selCs.isAlwaysEmpty || selType.widen.isBoxedCapturing || qualType.isBoxedCapturing then
295
309
selType
296
310
else
297
311
val qualCs = qualType.captureSet
298
312
capt.println(i " intersect $qualType, ${selType.widen}, $qualCs, $selCs in $tree" )
299
- if qualCs.mightSubcapture(selCs) then
313
+ if qualCs.mightSubcapture(selCs)
314
+ && ! selCs.mightSubcapture(qualCs)
315
+ && ! pt.stripCapturing.isInstanceOf [SingletonType ]
316
+ then
300
317
selType.widen.stripCapturing.capturing(qualCs)
318
+ .showing(i " alternate type for select $tree: $selType --> $result, $qualCs / $selCs" , capt)
301
319
else
302
320
selType
303
321
}// .showing(i"recheck sel $tree, $qualType = $result")
@@ -314,23 +332,32 @@ class CheckCaptures extends Recheck, SymTransformer:
314
332
* and Cr otherwise.
315
333
*/
316
334
override def recheckApply (tree : Apply , pt : Type )(using Context ): Type =
317
- includeCallCaptures(tree.symbol, tree.srcPos)
318
- super .recheckApply(tree, pt) match
319
- case appType @ CapturingType (appType1, refs) =>
320
- tree.fun match
321
- case Select (qual, _)
322
- if ! tree.fun.symbol.isConstructor
323
- && ! qual.tpe.isBoxedCapturing
324
- && ! tree.args.exists(_.tpe.isBoxedCapturing)
325
- && qual.tpe.captureSet.mightSubcapture(refs)
326
- && tree.args.forall(_.tpe.captureSet.mightSubcapture(refs))
327
- =>
328
- val callCaptures = tree.args.foldLeft(qual.tpe.captureSet)((cs, arg) =>
329
- cs ++ arg.tpe.captureSet)
330
- appType.derivedCapturingType(appType1, callCaptures)
331
- .showing(i " narrow $tree: $appType, refs = $refs, qual = ${qual.tpe.captureSet} --> $result" , capt)
332
- case _ => appType
333
- case appType => appType
335
+ val meth = tree.fun.symbol
336
+ includeCallCaptures(meth, tree.srcPos)
337
+ if meth == defn.Caps_unsafeBox || meth == defn.Caps_unsafeUnbox then
338
+ val arg :: Nil = tree.args: @ unchecked
339
+ val argType0 = recheckStart(arg, pt)
340
+ .forceBoxStatus(boxed = meth == defn.Caps_unsafeBox )
341
+ val argType = super .recheckFinish(argType0, arg, pt)
342
+ super .recheckFinish(argType, tree, pt)
343
+ else
344
+ super .recheckApply(tree, pt) match
345
+ case appType @ CapturingType (appType1, refs) =>
346
+ tree.fun match
347
+ case Select (qual, _)
348
+ if ! tree.fun.symbol.isConstructor
349
+ && ! qual.tpe.isBoxedCapturing
350
+ && ! tree.args.exists(_.tpe.isBoxedCapturing)
351
+ && qual.tpe.captureSet.mightSubcapture(refs)
352
+ && tree.args.forall(_.tpe.captureSet.mightSubcapture(refs))
353
+ =>
354
+ val callCaptures = tree.args.foldLeft(qual.tpe.captureSet)((cs, arg) =>
355
+ cs ++ arg.tpe.captureSet)
356
+ appType.derivedCapturingType(appType1, callCaptures)
357
+ .showing(i " narrow $tree: $appType, refs = $refs, qual = ${qual.tpe.captureSet} --> $result" , capt)
358
+ case _ => appType
359
+ case appType => appType
360
+ end recheckApply
334
361
335
362
/** Handle an application of method `sym` with type `mt` to arguments of types `argTypes`.
336
363
* This means:
@@ -435,10 +462,25 @@ class CheckCaptures extends Recheck, SymTransformer:
435
462
case _ =>
436
463
super .recheckBlock(block, pt)
437
464
465
+ /** If `rhsProto` has `*` as its capture set, wrap `rhs` in a `unsafeBox`.
466
+ * Used to infer `unsafeBox` for expressions that get assigned to variables
467
+ * that have universal capture set.
468
+ */
469
+ def maybeBox (rhs : Tree , rhsProto : Type )(using Context ): Tree =
470
+ if rhsProto.captureSet.isUniversal then
471
+ ref(defn.Caps_unsafeBox ).appliedToType(rhsProto).appliedTo(rhs)
472
+ else rhs
473
+
474
+ override def recheckAssign (tree : Assign )(using Context ): Type =
475
+ val rhsProto = recheck(tree.lhs).widen
476
+ recheck(maybeBox(tree.rhs, rhsProto), rhsProto)
477
+ defn.UnitType
478
+
438
479
override def recheckValDef (tree : ValDef , sym : Symbol )(using Context ): Unit =
439
480
try
440
481
if ! sym.is(Module ) then // Modules are checked by checking the module class
441
- super .recheckValDef(tree, sym)
482
+ if sym.is(Mutable ) then recheck(maybeBox(tree.rhs, sym.info), sym.info)
483
+ else super .recheckValDef(tree, sym)
442
484
finally
443
485
if ! sym.is(Param ) then
444
486
// Parameters with inferred types belong to anonymous methods. We need to wait
@@ -541,8 +583,6 @@ class CheckCaptures extends Recheck, SymTransformer:
541
583
tpe
542
584
case _ : Try =>
543
585
tpe
544
- case _ : ValDef if tree.symbol.is(Mutable ) =>
545
- tree.symbol.info
546
586
case _ =>
547
587
NoType
548
588
def checkNotUniversal (tp : Type ): Unit = tp.widenDealias match
@@ -671,7 +711,7 @@ class CheckCaptures extends Recheck, SymTransformer:
671
711
672
712
/** Destruct a capturing type `tp` to a tuple (cs, tp0, boxed),
673
713
* where `tp0` is not a capturing type.
674
- *
714
+ *
675
715
* If `tp` is a nested capturing type, the return tuple always represents
676
716
* the innermost capturing type. The outer capture annotations can be
677
717
* reconstructed with the returned function.
@@ -727,7 +767,7 @@ class CheckCaptures extends Recheck, SymTransformer:
727
767
val criticalSet = // the set which is not allowed to have `*`
728
768
if covariant then cs1 // can't box with `*`
729
769
else expected.captureSet // can't unbox with `*`
730
- if criticalSet.isUniversal then
770
+ if criticalSet.isUniversal && expected.isValueType then
731
771
// We can't box/unbox the universal capability. Leave `actual` as it is
732
772
// so we get an error in checkConforms. This tends to give better error
733
773
// messages than disallowing the root capability in `criticalSet`.
@@ -748,7 +788,6 @@ class CheckCaptures extends Recheck, SymTransformer:
748
788
recon(CapturingType (parent1, cs1, actualIsBoxed))
749
789
}
750
790
751
-
752
791
var actualw = actual.widenDealias
753
792
actual match
754
793
case ref : CaptureRef if ref.isTracked =>
@@ -768,6 +807,7 @@ class CheckCaptures extends Recheck, SymTransformer:
768
807
override def checkUnit (unit : CompilationUnit )(using Context ): Unit =
769
808
Setup (preRecheckPhase, thisPhase, recheckDef)
770
809
.traverse(ctx.compilationUnit.tpdTree)
810
+ // println(i"SETUP:\n${Recheck.addRecheckedTypes.transform(ctx.compilationUnit.tpdTree)}")
771
811
withCaptureSetsExplained {
772
812
super .checkUnit(unit)
773
813
checkSelfTypes(unit.tpdTree)
0 commit comments