@@ -5,7 +5,7 @@ package cc
55import core .*
66import Phases .* , DenotTransformers .* , SymDenotations .*
77import Contexts .* , Names .* , Flags .* , Symbols .* , Decorators .*
8- import Types .* , StdNames .*
8+ import Types .* , StdNames .* , Denotations . *
99import config .Printers .{capt , recheckr }
1010import config .Config
1111import ast .{tpd , untpd , Trees }
@@ -89,7 +89,7 @@ object CheckCaptures:
8989 elem.tpe match
9090 case ref : CaptureRef =>
9191 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)
9393 case tpe =>
9494 report.error(em " $elem: $tpe is not a legal element of a capture set " , elem.srcPos)
9595
@@ -288,16 +288,34 @@ class CheckCaptures extends Recheck, SymTransformer:
288288 * outcome of a `mightSubcapture` test. It picks `{f}` if this might subcapture Cr
289289 * and Cr otherwise.
290290 */
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)
293307 val selCs = selType.widen.captureSet
294308 if selCs.isAlwaysEmpty || selType.widen.isBoxedCapturing || qualType.isBoxedCapturing then
295309 selType
296310 else
297311 val qualCs = qualType.captureSet
298312 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
300317 selType.widen.stripCapturing.capturing(qualCs)
318+ .showing(i " alternate type for select $tree: $selType --> $result, $qualCs / $selCs" , capt)
301319 else
302320 selType
303321 }// .showing(i"recheck sel $tree, $qualType = $result")
@@ -314,23 +332,32 @@ class CheckCaptures extends Recheck, SymTransformer:
314332 * and Cr otherwise.
315333 */
316334 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
334361
335362 /** Handle an application of method `sym` with type `mt` to arguments of types `argTypes`.
336363 * This means:
@@ -435,10 +462,25 @@ class CheckCaptures extends Recheck, SymTransformer:
435462 case _ =>
436463 super .recheckBlock(block, pt)
437464
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+
438479 override def recheckValDef (tree : ValDef , sym : Symbol )(using Context ): Unit =
439480 try
440481 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)
442484 finally
443485 if ! sym.is(Param ) then
444486 // Parameters with inferred types belong to anonymous methods. We need to wait
@@ -541,8 +583,6 @@ class CheckCaptures extends Recheck, SymTransformer:
541583 tpe
542584 case _ : Try =>
543585 tpe
544- case _ : ValDef if tree.symbol.is(Mutable ) =>
545- tree.symbol.info
546586 case _ =>
547587 NoType
548588 def checkNotUniversal (tp : Type ): Unit = tp.widenDealias match
@@ -671,7 +711,7 @@ class CheckCaptures extends Recheck, SymTransformer:
671711
672712 /** Destruct a capturing type `tp` to a tuple (cs, tp0, boxed),
673713 * where `tp0` is not a capturing type.
674- *
714+ *
675715 * If `tp` is a nested capturing type, the return tuple always represents
676716 * the innermost capturing type. The outer capture annotations can be
677717 * reconstructed with the returned function.
@@ -727,7 +767,7 @@ class CheckCaptures extends Recheck, SymTransformer:
727767 val criticalSet = // the set which is not allowed to have `*`
728768 if covariant then cs1 // can't box with `*`
729769 else expected.captureSet // can't unbox with `*`
730- if criticalSet.isUniversal then
770+ if criticalSet.isUniversal && expected.isValueType then
731771 // We can't box/unbox the universal capability. Leave `actual` as it is
732772 // so we get an error in checkConforms. This tends to give better error
733773 // messages than disallowing the root capability in `criticalSet`.
@@ -748,7 +788,6 @@ class CheckCaptures extends Recheck, SymTransformer:
748788 recon(CapturingType (parent1, cs1, actualIsBoxed))
749789 }
750790
751-
752791 var actualw = actual.widenDealias
753792 actual match
754793 case ref : CaptureRef if ref.isTracked =>
@@ -768,6 +807,7 @@ class CheckCaptures extends Recheck, SymTransformer:
768807 override def checkUnit (unit : CompilationUnit )(using Context ): Unit =
769808 Setup (preRecheckPhase, thisPhase, recheckDef)
770809 .traverse(ctx.compilationUnit.tpdTree)
810+ // println(i"SETUP:\n${Recheck.addRecheckedTypes.transform(ctx.compilationUnit.tpdTree)}")
771811 withCaptureSetsExplained {
772812 super .checkUnit(unit)
773813 checkSelfTypes(unit.tpdTree)
0 commit comments