Skip to content

Commit 1c79148

Browse files
authored
Check conformance to caps.Pure upper bound only under cc (#23784)
This is necessary to ensure source compatibility of the capture checked stdlib.
1 parent 1f13619 commit 1c79148

File tree

5 files changed

+70
-2
lines changed

5 files changed

+70
-2
lines changed

compiler/src/dotty/tools/dotc/core/TypeOps.scala

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -706,9 +706,34 @@ object TypeOps:
706706
def loop(args: List[Tree], boundss: List[TypeBounds]): Unit = args match
707707
case arg :: args1 => boundss match
708708
case bounds :: boundss1 =>
709+
710+
// Drop caps.Pure from a bound (1) at the top-level, (2) in an `&`, (3) under a type lambda.
711+
def dropPure(tp: Type): Option[Type] = tp match
712+
case tp @ AndType(tp1, tp2) =>
713+
dropPure(tp1) match
714+
case Some(tp1o) =>
715+
dropPure(tp2) match
716+
case Some(tp2o) => Some(tp.derivedAndType(tp1o, tp2o))
717+
case None => Some(tp1o)
718+
case None =>
719+
dropPure(tp2)
720+
case tp: HKTypeLambda =>
721+
for rt <- dropPure(tp.resType) yield
722+
tp.derivedLambdaType(resType = rt)
723+
case _ =>
724+
if tp.typeSymbol == defn.PureClass then None
725+
else Some(tp)
726+
727+
val relevantBounds =
728+
if Feature.ccEnabled then bounds
729+
else
730+
// Drop caps.Pure from bound, it should be checked only when capture checking is enabled
731+
dropPure(bounds.hi).match
732+
case Some(hi1) => bounds.derivedTypeBounds(bounds.lo, hi1)
733+
case None => TypeBounds(bounds.lo, defn.AnyKindType)
709734
arg.tpe match
710-
case TypeBounds(lo, hi) => checkOverlapsBounds(lo, hi, arg, bounds)
711-
case tp => checkOverlapsBounds(tp, tp, arg, bounds)
735+
case TypeBounds(lo, hi) => checkOverlapsBounds(lo, hi, arg, relevantBounds)
736+
case tp => checkOverlapsBounds(tp, tp, arg, relevantBounds)
712737
loop(args1, boundss1)
713738
case _ =>
714739
case _ =>

library/src/scala/caps/Pure.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ import language.experimental.captureChecking
66
/** A marker trait that declares that all inheriting classes are "pure" in the
77
* sense that their values retain no capabilities including capabilities needed
88
* to perform effects. This has formal meaning only under capture checking.
9+
*
10+
* NOTE: If an upper bound is Pure, we check that an argument conforms to it only
11+
* in sources where capture checking is enabled. For instance,
12+
*
13+
* def f[C <: Pure]()
14+
* f[Object]()
15+
*
16+
* would give an error only under capture checking. Pure is also dropped in
17+
* upper bounds if it forms part of an &-type, or is under a type lambda.
918
*/
1019
trait Pure extends Any:
1120
this: Pure =>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import caps.Pure
2+
class P extends Pure
3+
def foo[C <: Pure]() = ()
4+
def bar[T, C <: Iterable[T] & Pure]() = ()
5+
def baz[CC[_] <: Pure]() = ()
6+
def bam[CC[A] <: Pure & Iterable[A]]() = ()
7+
def test =
8+
foo[Int]() // error
9+
bar[Int, List[Int]]() // error
10+
baz[Seq]() // error
11+
bam[Seq]() // error
12+
foo[P]() // OK

tests/neg/puretest.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import caps.Pure
2+
def foo[C <: Pure]() = ()
3+
def bar[T, C <: Iterable[T] & Pure]() = ()
4+
def baz[CC[_] <: Pure]() = ()
5+
def bam[CC[A] <: Pure & Iterable[A]]() = ()
6+
def test =
7+
foo[Int]()
8+
bar[Int, List[Int]]()
9+
bar[Int, Iterator[Int]]() // error
10+
baz[Seq]()
11+
bam[Seq]()
12+
bam[Iterator]() // error

tests/pos/puretest.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import caps.Pure
2+
def foo[C <: Pure]() = ()
3+
def bar[T, C <: Iterable[T] & Pure]() = ()
4+
def baz[CC[_] <: Pure]() = ()
5+
def bam[CC[A] <: Pure & Iterable[A]]() = ()
6+
def test =
7+
foo[Int]()
8+
bar[Int, List[Int]]()
9+
baz[Seq]()
10+
bam[Seq]()

0 commit comments

Comments
 (0)