@@ -1807,65 +1807,26 @@ class CheckCaptures extends Recheck, SymTransformer:
1807
1807
capt.println(i " checked $root with $selfType" )
1808
1808
end checkSelfTypes
1809
1809
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.
1827
1815
*/
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 =
1829
1817
val checker = new TypeTraverser :
1830
1818
private var allowed : SimpleIdentitySet [TermParamRef ] = SimpleIdentitySet .empty
1831
1819
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 _ =>
1864
1825
1865
1826
def traverse (tp : Type ) =
1866
1827
tp match
1867
1828
case CapturingType (parent, refs) =>
1868
- healCaptureSet (refs)
1829
+ checkCaptureSet (refs)
1869
1830
traverse(parent)
1870
1831
case defn.RefinedFunctionOf (rinfo : MethodType ) =>
1871
1832
traverse(rinfo)
@@ -1880,7 +1841,7 @@ class CheckCaptures extends Recheck, SymTransformer:
1880
1841
1881
1842
if tree.isInstanceOf [InferredTypeTree ] then
1882
1843
checker.traverse(tree.nuType)
1883
- end healTypeParam
1844
+ end checkTypeParam
1884
1845
1885
1846
/** Under the unsealed policy: Arrays are like vars, check that their element types
1886
1847
* do not contains `cap` (in fact it would work also to check on array creation
@@ -1904,9 +1865,7 @@ class CheckCaptures extends Recheck, SymTransformer:
1904
1865
traverseChildren(t)
1905
1866
check.traverse(tp)
1906
1867
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.
1910
1869
*/
1911
1870
def postCheck (unit : tpd.Tree )(using Context ): Unit =
1912
1871
val checker = new TreeTraverser :
@@ -1926,7 +1885,8 @@ class CheckCaptures extends Recheck, SymTransformer:
1926
1885
bounds.hi.isBoxedCapturing | bounds.lo.isBoxedCapturing))
1927
1886
CCState .withCapAsRoot: // OK? We need this since bounds use `cap` instead of `fresh`
1928
1887
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))
1930
1890
case _ =>
1931
1891
case _ =>
1932
1892
end check
0 commit comments