@@ -1807,65 +1807,26 @@ class CheckCaptures extends Recheck, SymTransformer:
18071807 capt.println(i " checked $root with $selfType" )
18081808 end checkSelfTypes
18091809
1810- /** Heal ill-formed capture sets in the type parameter.
1811- *
1812- * We can push parameter refs into a capture set in type parameters
1813- * that this type parameter can't see.
1814- * For example, when capture checking the following expression:
1815- *
1816- * def usingLogFile[T](op: File^ => T): T = ...
1817- *
1818- * usingLogFile[box ?1 () -> Unit] { (f: File^) => () => { f.write(0) } }
1819- *
1820- * We may propagate `f` into ?1, making ?1 ill-formed.
1821- * This also causes soundness issues, since `f` in ?1 should be widened to `cap`,
1822- * giving rise to an error that `cap` cannot be included in a boxed capture set.
1823- *
1824- * To solve this, we still allow ?1 to capture parameter refs like `f`, but
1825- * compensate this by pushing the widened capture set of `f` into ?1.
1826- * This solves the soundness issue caused by the ill-formness of ?1.
1810+ /** Check ill-formed capture sets in a type parameter. We used to be able to
1811+ * push parameter refs into a capture set in type parameters that this type
1812+ * parameter can't see. We used to heal this by replacing illegal refs by their
1813+ * underlying capture sets. But now these should no longer be necessary, so
1814+ * instead of errors we use assertions.
18271815 */
1828- private def healTypeParam (tree : Tree , paramName : TypeName , meth : Symbol )(using Context ): Unit =
1816+ private def checkTypeParam (tree : Tree , paramName : TypeName , meth : Symbol )(using Context ): Unit =
18291817 val checker = new TypeTraverser :
18301818 private var allowed : SimpleIdentitySet [TermParamRef ] = SimpleIdentitySet .empty
18311819
1832- private def isAllowed (ref : CaptureRef ): Boolean = ref match
1833- case ref : TermParamRef => allowed.contains(ref)
1834- case _ => true
1835-
1836- private def healCaptureSet (cs : CaptureSet ): Unit =
1837- cs.ensureWellformed: elem =>
1838- ctx ?=>
1839- var seen = new util.HashSet [CaptureRef ]
1840- def recur (ref : CaptureRef ): Unit = ref.stripReach match
1841- case ref : TermParamRef
1842- if ! allowed.contains(ref) && ! seen.contains(ref) =>
1843- seen += ref
1844- if ref.isRootCapability then
1845- report.error(i " escaping local reference $ref" , tree.srcPos)
1846- else
1847- val widened = ref.captureSetOfInfo
1848- val added = widened.filter(isAllowed(_))
1849- capt.println(i " heal $ref in $cs by widening to $added" )
1850- if ! added.subCaptures(cs).isOK then
1851- val location = if meth.exists then i " of ${meth.showLocated}" else " "
1852- val paramInfo =
1853- if ref.paramName.info.kind.isInstanceOf [UniqueNameKind ]
1854- then i " ${ref.paramName} from ${ref.binder}"
1855- else i " ${ref.paramName}"
1856- val debugSetInfo = if ctx.settings.YccDebug .value then i " $cs" else " "
1857- report.error(
1858- i " local reference $paramInfo leaks into outer capture set $debugSetInfo of type parameter $paramName$location" ,
1859- tree.srcPos)
1860- else
1861- widened.elems.foreach(recur)
1862- case _ =>
1863- recur(elem)
1820+ private def checkCaptureSet (cs : CaptureSet ): Unit =
1821+ for elem <- cs.elems do
1822+ elem.stripReach match
1823+ case ref : TermParamRef => assert(allowed.contains(ref))
1824+ case _ =>
18641825
18651826 def traverse (tp : Type ) =
18661827 tp match
18671828 case CapturingType (parent, refs) =>
1868- healCaptureSet (refs)
1829+ checkCaptureSet (refs)
18691830 traverse(parent)
18701831 case defn.RefinedFunctionOf (rinfo : MethodType ) =>
18711832 traverse(rinfo)
@@ -1880,7 +1841,7 @@ class CheckCaptures extends Recheck, SymTransformer:
18801841
18811842 if tree.isInstanceOf [InferredTypeTree ] then
18821843 checker.traverse(tree.nuType)
1883- end healTypeParam
1844+ end checkTypeParam
18841845
18851846 /** Under the unsealed policy: Arrays are like vars, check that their element types
18861847 * do not contains `cap` (in fact it would work also to check on array creation
@@ -1904,9 +1865,7 @@ class CheckCaptures extends Recheck, SymTransformer:
19041865 traverseChildren(t)
19051866 check.traverse(tp)
19061867
1907- /** Perform the following kinds of checks
1908- * - Check that arguments of TypeApplys and AppliedTypes conform to their bounds.
1909- * - Heal ill-formed capture sets of type parameters. See `healTypeParam`.
1868+ /** Check that arguments of TypeApplys and AppliedTypes conform to their bounds.
19101869 */
19111870 def postCheck (unit : tpd.Tree )(using Context ): Unit =
19121871 val checker = new TreeTraverser :
@@ -1926,7 +1885,8 @@ class CheckCaptures extends Recheck, SymTransformer:
19261885 bounds.hi.isBoxedCapturing | bounds.lo.isBoxedCapturing))
19271886 CCState .withCapAsRoot: // OK? We need this since bounds use `cap` instead of `fresh`
19281887 checkBounds(normArgs, tl)
1929- args.lazyZip(tl.paramNames).foreach(healTypeParam(_, _, fun.symbol))
1888+ if ccConfig.postCheckCapturesets then
1889+ args.lazyZip(tl.paramNames).foreach(checkTypeParam(_, _, fun.symbol))
19301890 case _ =>
19311891 case _ =>
19321892 end check
0 commit comments