Skip to content

Commit

Permalink
Tweaks to path checking and massage tests
Browse files Browse the repository at this point in the history
Needed to make stdlib2-cc go through.

There were two errors. One in LayListIterable required a type annotation
and a tweak to markFree. The other in Vieew.scala required a cast, but this could be fixed
with better handling of pattern matching. path-patmat-should-be-pos.scala is a minimization.
  • Loading branch information
odersky committed Sep 25, 2024
1 parent 263d6eb commit 152710b
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 18 deletions.
27 changes: 18 additions & 9 deletions compiler/src/dotty/tools/dotc/cc/CaptureRef.scala
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,19 @@ trait CaptureRef extends TypeProxy, ValueType:
* TODO: Document path cases
*/
final def subsumes(y: CaptureRef)(using Context): Boolean =

def subsumingRefs(x: Type, y: Type): Boolean = x match
case x: CaptureRef => y match
case y: CaptureRef => x.subsumes(y)
case _ => false
case _ => false

def viaInfo(info: Type)(test: Type => Boolean): Boolean = info.match
case info: SingletonCaptureRef => test(info)
case info: AndType => test(info.tp1) || test(info.tp2)
case info: OrType => test(info.tp1) && test(info.tp2)
case _ => false

(this eq y)
|| this.isRootCapability
|| y.match
Expand All @@ -109,25 +122,21 @@ trait CaptureRef extends TypeProxy, ValueType:
case ypre: CaptureRef =>
this.subsumes(ypre)
|| this.match
case x @ TermRef(xpre: CaptureRef, _) =>
x.symbol == y.symbol && xpre =:= ypre
case x @ TermRef(xpre: CaptureRef, _) if x.symbol == y.symbol =>
subsumingRefs(xpre, ypre) && subsumingRefs(ypre, xpre)
case _ =>
false
case _ => false
|| y.info.match
case y1: SingletonCaptureRef => this.subsumes(y1)
case _ => false
|| viaInfo(y.info)(subsumingRefs(this, _))
case MaybeCapability(y1) => this.stripMaybe.subsumes(y1)
case _ => false
|| this.match
case ReachCapability(x1) => x1.subsumes(y.stripReach)
case x: TermRef =>
x.info match
case x1: SingletonCaptureRef => x1.subsumes(y)
case _ => false
case x: TermRef => viaInfo(x.info)(subsumingRefs(_, y))
case x: TermParamRef => subsumesExistentially(x, y)
case x: TypeRef => assumedContainsOf(x).contains(y)
case _ => false
end subsumes

def assumedContainsOf(x: TypeRef)(using Context): SimpleIdentitySet[CaptureRef] =
CaptureSet.assumedContains.getOrElse(x, SimpleIdentitySet.empty)
Expand Down
25 changes: 18 additions & 7 deletions compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -466,16 +466,24 @@ class CheckCaptures extends Recheck, SymTransformer:
if tree.symbol.info.isParameterless then
// there won't be an apply; need to include call captures now
includeCallCaptures(tree.symbol, tree.srcPos)
else
else if !tree.symbol.isStatic then
//debugShowEnvs()
def addSelects(ref: TermRef, pt: Type): TermRef = pt match
case pt: PathSelectionProto => addSelects(ref.select(pt.sym).asInstanceOf[TermRef], pt.pt)
case pt: PathSelectionProto if ref.isTracked =>
// if `ref` is not tracked then the selection could not give anything new
// class SerializationProxy in stdlib-cc/../LazyListIterable.scala has an example where this matters.
addSelects(ref.select(pt.sym).asInstanceOf[TermRef], pt.pt)
case _ => ref
markFree(tree.symbol, addSelects(tree.symbol.termRef, pt), tree.srcPos)
val ref = tree.symbol.termRef
val pathRef = addSelects(ref, pt)
//if pathRef ne ref then
// println(i"add selects $ref --> $pathRef")
markFree(tree.symbol, if false then ref else pathRef, tree.srcPos)
super.recheckIdent(tree, pt)

override def selectionProto(tree: Select, pt: Type)(using Context): Type =
if !tree.symbol.isOneOf(UnstableValueFlags) then PathSelectionProto(tree.symbol, pt)
val sym = tree.symbol
if !sym.isOneOf(UnstableValueFlags) && !sym.isStatic then PathSelectionProto(sym, pt)
else super.selectionProto(tree, pt)

/** A specialized implementation of the selection rule.
Expand Down Expand Up @@ -1141,11 +1149,14 @@ class CheckCaptures extends Recheck, SymTransformer:
(erefs /: erefs.elems): (erefs, eref) =>
eref match
case eref: ThisType if isPureContext(ctx.owner, eref.cls) =>
erefs ++ arefs.filter {
case aref: TermRef => eref.cls.isProperlyContainedIn(aref.symbol.owner)
def isOuterRef(aref: Type): Boolean = aref match
case aref: TermRef =>
val owner = aref.symbol.owner
if owner.isClass then isOuterRef(aref.prefix)
else eref.cls.isProperlyContainedIn(owner)
case aref: ThisType => eref.cls.isProperlyContainedIn(aref.cls)
case _ => false
}
erefs ++ arefs.filter(isOuterRef)
case _ =>
erefs

Expand Down
5 changes: 4 additions & 1 deletion scala2-library-cc/src/scala/collection/View.scala
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,10 @@ object View extends IterableFactory[View] {
object Filter {
def apply[A](underlying: Iterable[A]^, p: A => Boolean, isFlipped: Boolean): Filter[A]^{underlying, p} =
underlying match {
case filter: Filter[A] if filter.isFlipped == isFlipped => new Filter(filter.underlying, a => filter.p(a) && p(a), isFlipped)
case filter: Filter[A] if filter.isFlipped == isFlipped =>
new Filter(filter.underlying, a => filter.p(a) && p(a), isFlipped)
.asInstanceOf[Filter[A]^{underlying, p}]
// !!! asInstanceOf needed once paths were added, see path-patmat-should-be-pos.scala for minimization
case _ => new Filter(underlying, p, isFlipped)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1366,7 +1366,9 @@ object LazyListIterable extends IterableFactory[LazyListIterable] {
case SerializeEnd => initRead = true
case a => init += a.asInstanceOf[A]
}
val tail = in.readObject().asInstanceOf[LazyListIterable[A]]
val tail: LazyListIterable[A] = in.readObject().asInstanceOf[LazyListIterable[A]]
// Explicit type annotation needed so that tail.state below is dropped from capture set.
// Before paths were added, it was tail that was added, and the `asSeenFrom` to a pure type made it work.
// scala/scala#10118: caution that no code path can evaluate `tail.state`
// before the resulting LazyListIterable is returned
val it = init.toList.iterator
Expand Down
26 changes: 26 additions & 0 deletions tests/neg-custom-args/captures/path-patmat-should-be-pos.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class It[A]

class Filter[A](val underlying: It[A]^, val p: A => Boolean) extends It[A]
object Filter:
def apply[A](underlying: It[A]^, p: A => Boolean): Filter[A]^{underlying, p} =
underlying match
case filter: Filter[A]^ =>
val x = new Filter(filter.underlying, a => filter.p(a) && p(a))
x: Filter[A]^{underlying, p} // error
// !!! should work, it seems to be the case that the system does not recognize that
// underlying and filter are aliases.

// On the other hand, the following works:
locally:
val filter: underlying.type & Filter[A] = ???
val a: It[A]^{filter.underlying} = ???
val b: It[A]^{underlying} = a
val x = new Filter(filter.underlying, a => filter.p(a) && p(a))
x: Filter[A]^{underlying, p}

locally:
val filter: underlying.type & Filter[A]^ = ???
val a: It[A]^{filter.underlying} = ???
val b: It[A]^{underlying} = a
val x = new Filter(filter.underlying, a => filter.p(a) && p(a))
x: Filter[A]^{underlying, p}

0 comments on commit 152710b

Please sign in to comment.