Skip to content

Commit

Permalink
Avoid using Ranges in exact substitutions (scala#17532)
Browse files Browse the repository at this point in the history
  • Loading branch information
Linyxus authored May 22, 2023
2 parents f2f1156 + 113b08a commit fe08f5f
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 10 deletions.
27 changes: 17 additions & 10 deletions compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,23 @@ object CheckCaptures:
*/
final class SubstParamsMap(from: BindingType, to: List[Type])(using Context)
extends ApproximatingTypeMap, IdempotentCaptRefMap:
def apply(tp: Type): Type = tp match
case tp: ParamRef =>
if tp.binder == from then to(tp.paramNum) else tp
case tp: NamedType =>
if tp.prefix `eq` NoPrefix then tp
else tp.derivedSelect(apply(tp.prefix))
case _: ThisType =>
tp
case _ =>
mapOver(tp)
/** This SubstParamsMap is exact if `to` only contains `CaptureRef`s. */
private val isExactSubstitution: Boolean = to.forall(_.isInstanceOf[CaptureRef])

/** As long as this substitution is exact, there is no need to create `Range`s when mapping invariant positions. */
override protected def needsRangeIfInvariant(refs: CaptureSet): Boolean = !isExactSubstitution

def apply(tp: Type): Type =
tp match
case tp: ParamRef =>
if tp.binder == from then to(tp.paramNum) else tp
case tp: NamedType =>
if tp.prefix `eq` NoPrefix then tp
else tp.derivedSelect(apply(tp.prefix))
case _: ThisType =>
tp
case _ =>
mapOver(tp)

/** Check that a @retains annotation only mentions references that can be tracked.
* This check is performed at Typer.
Expand Down
33 changes: 33 additions & 0 deletions tests/neg-custom-args/captures/cc-subst-param-exact.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import language.experimental.captureChecking
import caps.*

trait Ref[T] { def set(x: T): T }
def test() = {

def swap[T](x: Ref[T]^)(y: Ref[T]^{x}): Unit = ???
def foo[T](x: Ref[T]^): Unit =
swap(x)(x)

def bar[T](x: () => Ref[T]^)(y: Ref[T]^{x}): Unit =
swap(x())(y) // error

def baz[T](x: Ref[T]^)(y: Ref[T]^{x}): Unit =
swap(x)(y)
}

trait IO
type Op = () -> Unit
def test2(c: IO^, f: Op^{c}) = {
def run(io: IO^)(op: Op^{io}): Unit = op()
run(c)(f)

def bad(getIO: () => IO^, g: Op^{getIO}): Unit =
run(getIO())(g) // error
}

def test3() = {
def run(io: IO^)(op: Op^{io}): Unit = ???
val myIO: IO^ = ???
val myOp: Op^{myIO} = ???
run(myIO)(myOp)
}

0 comments on commit fe08f5f

Please sign in to comment.