Skip to content

Commit 3224b21

Browse files
committed
Tweaks to path checking and massage tests
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.
1 parent 54c0242 commit 3224b21

File tree

5 files changed

+69
-18
lines changed

5 files changed

+69
-18
lines changed

compiler/src/dotty/tools/dotc/cc/CaptureRef.scala

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,19 @@ trait CaptureRef extends TypeProxy, ValueType:
101101
* TODO: Document path cases
102102
*/
103103
final def subsumes(y: CaptureRef)(using Context): Boolean =
104+
105+
def subsumingRefs(x: Type, y: Type): Boolean = x match
106+
case x: CaptureRef => y match
107+
case y: CaptureRef => x.subsumes(y)
108+
case _ => false
109+
case _ => false
110+
111+
def viaInfo(info: Type)(test: Type => Boolean): Boolean = info.match
112+
case info: SingletonCaptureRef => test(info)
113+
case info: AndType => test(info.tp1) || test(info.tp2)
114+
case info: OrType => test(info.tp1) && test(info.tp2)
115+
case _ => false
116+
104117
(this eq y)
105118
|| this.isRootCapability
106119
|| y.match
@@ -109,25 +122,21 @@ trait CaptureRef extends TypeProxy, ValueType:
109122
case ypre: CaptureRef =>
110123
this.subsumes(ypre)
111124
|| this.match
112-
case x @ TermRef(xpre: CaptureRef, _) =>
113-
x.symbol == y.symbol && xpre =:= ypre
125+
case x @ TermRef(xpre: CaptureRef, _) if x.symbol == y.symbol =>
126+
subsumingRefs(xpre, ypre) && subsumingRefs(ypre, xpre)
114127
case _ =>
115128
false
116129
case _ => false
117-
|| y.info.match
118-
case y1: SingletonCaptureRef => this.subsumes(y1)
119-
case _ => false
130+
|| viaInfo(y.info)(subsumingRefs(this, _))
120131
case MaybeCapability(y1) => this.stripMaybe.subsumes(y1)
121132
case _ => false
122133
|| this.match
123134
case ReachCapability(x1) => x1.subsumes(y.stripReach)
124-
case x: TermRef =>
125-
x.info match
126-
case x1: SingletonCaptureRef => x1.subsumes(y)
127-
case _ => false
135+
case x: TermRef => viaInfo(x.info)(subsumingRefs(_, y))
128136
case x: TermParamRef => subsumesExistentially(x, y)
129137
case x: TypeRef => assumedContainsOf(x).contains(y)
130138
case _ => false
139+
end subsumes
131140

132141
def assumedContainsOf(x: TypeRef)(using Context): SimpleIdentitySet[CaptureRef] =
133142
CaptureSet.assumedContains.getOrElse(x, SimpleIdentitySet.empty)

compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -466,16 +466,24 @@ class CheckCaptures extends Recheck, SymTransformer:
466466
if tree.symbol.info.isParameterless then
467467
// there won't be an apply; need to include call captures now
468468
includeCallCaptures(tree.symbol, tree.srcPos)
469-
else
469+
else if !tree.symbol.isStatic then
470470
//debugShowEnvs()
471471
def addSelects(ref: TermRef, pt: Type): TermRef = pt match
472-
case pt: PathSelectionProto => addSelects(ref.select(pt.sym).asInstanceOf[TermRef], pt.pt)
472+
case pt: PathSelectionProto if ref.isTracked =>
473+
// if `ref` is not tracked then the selection could not give anything new
474+
// class SerializationProxy in stdlib-cc/../LazyListIterable.scala has an example where this matters.
475+
addSelects(ref.select(pt.sym).asInstanceOf[TermRef], pt.pt)
473476
case _ => ref
474-
markFree(tree.symbol, addSelects(tree.symbol.termRef, pt), tree.srcPos)
477+
val ref = tree.symbol.termRef
478+
val pathRef = addSelects(ref, pt)
479+
//if pathRef ne ref then
480+
// println(i"add selects $ref --> $pathRef")
481+
markFree(tree.symbol, if false then ref else pathRef, tree.srcPos)
475482
super.recheckIdent(tree, pt)
476483

477484
override def selectionProto(tree: Select, pt: Type)(using Context): Type =
478-
if !tree.symbol.isOneOf(UnstableValueFlags) then PathSelectionProto(tree.symbol, pt)
485+
val sym = tree.symbol
486+
if !sym.isOneOf(UnstableValueFlags) && !sym.isStatic then PathSelectionProto(sym, pt)
479487
else super.selectionProto(tree, pt)
480488

481489
/** A specialized implementation of the selection rule.
@@ -1141,11 +1149,14 @@ class CheckCaptures extends Recheck, SymTransformer:
11411149
(erefs /: erefs.elems): (erefs, eref) =>
11421150
eref match
11431151
case eref: ThisType if isPureContext(ctx.owner, eref.cls) =>
1144-
erefs ++ arefs.filter {
1145-
case aref: TermRef => eref.cls.isProperlyContainedIn(aref.symbol.owner)
1152+
def isOuterRef(aref: Type): Boolean = aref match
1153+
case aref: TermRef =>
1154+
val owner = aref.symbol.owner
1155+
if owner.isClass then isOuterRef(aref.prefix)
1156+
else eref.cls.isProperlyContainedIn(owner)
11461157
case aref: ThisType => eref.cls.isProperlyContainedIn(aref.cls)
11471158
case _ => false
1148-
}
1159+
erefs ++ arefs.filter(isOuterRef)
11491160
case _ =>
11501161
erefs
11511162

scala2-library-cc/src/scala/collection/View.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,10 @@ object View extends IterableFactory[View] {
150150
object Filter {
151151
def apply[A](underlying: Iterable[A]^, p: A => Boolean, isFlipped: Boolean): Filter[A]^{underlying, p} =
152152
underlying match {
153-
case filter: Filter[A] if filter.isFlipped == isFlipped => new Filter(filter.underlying, a => filter.p(a) && p(a), isFlipped)
153+
case filter: Filter[A] if filter.isFlipped == isFlipped =>
154+
new Filter(filter.underlying, a => filter.p(a) && p(a), isFlipped)
155+
.asInstanceOf[Filter[A]^{underlying, p}]
156+
// !!! asInstanceOf needed once paths were added, see path-patmat-should-be-pos.scala for minimization
154157
case _ => new Filter(underlying, p, isFlipped)
155158
}
156159
}

scala2-library-cc/src/scala/collection/immutable/LazyListIterable.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1366,7 +1366,9 @@ object LazyListIterable extends IterableFactory[LazyListIterable] {
13661366
case SerializeEnd => initRead = true
13671367
case a => init += a.asInstanceOf[A]
13681368
}
1369-
val tail = in.readObject().asInstanceOf[LazyListIterable[A]]
1369+
val tail: LazyListIterable[A] = in.readObject().asInstanceOf[LazyListIterable[A]]
1370+
// Explicit type annotation needed so that tail.state below is dropped from capture set.
1371+
// Before paths were added, it was tail that was added, and the `asSeenFrom` to a pure type made it work.
13701372
// scala/scala#10118: caution that no code path can evaluate `tail.state`
13711373
// before the resulting LazyListIterable is returned
13721374
val it = init.toList.iterator
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
class It[A]
2+
3+
class Filter[A](val underlying: It[A]^, val p: A => Boolean) extends It[A]
4+
object Filter:
5+
def apply[A](underlying: It[A]^, p: A => Boolean): Filter[A]^{underlying, p} =
6+
underlying match
7+
case filter: Filter[A]^ =>
8+
val x = new Filter(filter.underlying, a => filter.p(a) && p(a))
9+
x: Filter[A]^{underlying, p} // error
10+
// !!! should work, it seems to be the case that the system does not recognize that
11+
// underlying and filter are aliases.
12+
13+
// On the other hand, the following works:
14+
locally:
15+
val filter: underlying.type & Filter[A] = ???
16+
val a: It[A]^{filter.underlying} = ???
17+
val b: It[A]^{underlying} = a
18+
val x = new Filter(filter.underlying, a => filter.p(a) && p(a))
19+
x: Filter[A]^{underlying, p}
20+
21+
locally:
22+
val filter: underlying.type & Filter[A]^ = ???
23+
val a: It[A]^{filter.underlying} = ???
24+
val b: It[A]^{underlying} = a
25+
val x = new Filter(filter.underlying, a => filter.p(a) && p(a))
26+
x: Filter[A]^{underlying, p}

0 commit comments

Comments
 (0)