Skip to content

Commit 7bf957d

Browse files
committed
Don't perform the apply rule optimization under boxing
This was already the case for the select rule optimization in recheckSelection but was missing for the corresponding rule for applications. This caused one regression in the standard library, here: ``` private def eagerHeadConcatIterators[A](it: Iterator[collection.Iterable[A]^]^): LazyListIterable[A]^{it} = if !it.hasNext then Empty else eagerHeadPrependIterator (it.next().iterator) (eagerHeadConcatIterators(it)) ``` Previously the access to `it.next()` was considered to have type `it` by applying the apply rule incorrectly. It should be `it*`. This means we now have `it*` instead of `it` in the result type and we also have an illegal use of `it*` leaking outside `eagerHeadConcatIterators`. The second problem was fixed by adding an unsafe escape hatch `unsafeDiscardUses` that suppressed use recording. This leads to: ``` private def eagerHeadConcatIterators[A](it: Iterator[collection.Iterable[A]^]^): LazyListIterable[A]^{it*} = if !it.hasNext then Empty else eagerHeadPrependIterator (caps.unsafe.unsafeDiscardUses(it.next()).iterator) (eagerHeadConcatIterators(it)) ``` This also did not compile since it claimed that the `it @reachCapability` was not a legal element of a capture set. The root cause was that we forced some definitions already in parser, which can lead to confusion when compiling the standard library itself. We now refrain from doing that and build the references to these annotations as untyped trees all the way down.
1 parent 3bda057 commit 7bf957d

File tree

8 files changed

+115
-9
lines changed

8 files changed

+115
-9
lines changed

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,10 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
523523
def rootDot(name: Name)(implicit src: SourceFile): Select = Select(Ident(nme.ROOTPKG), name)
524524
def scalaDot(name: Name)(implicit src: SourceFile): Select = Select(rootDot(nme.scala), name)
525525
def scalaAnnotationDot(name: Name)(using SourceFile): Select = Select(scalaDot(nme.annotation), name)
526+
def scalaAnnotationInternalDot(name: Name)(using SourceFile): Select = Select(scalaAnnotationDot(nme.internal), name)
526527
def scalaRuntimeDot(name: Name)(using SourceFile): Select = Select(scalaDot(nme.runtime), name)
528+
def scalaCapsDot(name: Name)(using SourceFile): Select = Select(scalaDot(nme.caps), name)
529+
def scalaCapsInternalDot(name: Name)(using SourceFile): Select = Select(scalaCapsDot(nme.internal), name)
527530
def scalaUnit(implicit src: SourceFile): Select = scalaDot(tpnme.Unit)
528531
def scalaAny(implicit src: SourceFile): Select = scalaDot(tpnme.Any)
529532

@@ -553,16 +556,16 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
553556
Annotated(parent, annot)
554557

555558
def makeReachAnnot()(using Context): Tree =
556-
New(ref(defn.ReachCapabilityAnnot.typeRef), Nil :: Nil)
559+
New(scalaAnnotationInternalDot(tpnme.reachCapability), Nil :: Nil)
557560

558561
def makeReadOnlyAnnot()(using Context): Tree =
559-
New(ref(defn.ReadOnlyCapabilityAnnot.typeRef), Nil :: Nil)
562+
New(scalaAnnotationInternalDot(tpnme.readOnlyCapability), Nil :: Nil)
560563

561564
def makeOnlyAnnot(qid: Tree)(using Context) =
562-
New(AppliedTypeTree(ref(defn.OnlyCapabilityAnnot.typeRef), qid :: Nil), Nil :: Nil)
565+
New(AppliedTypeTree(scalaAnnotationInternalDot(tpnme.onlyCapability), qid :: Nil), Nil :: Nil)
563566

564567
def makeConsumeAnnot()(using Context): Tree =
565-
New(ref(defn.ConsumeAnnot.typeRef), Nil :: Nil)
568+
New(scalaCapsInternalDot(tpnme.consume), Nil :: Nil)
566569

567570
def makeConstructor(tparams: List[TypeDef], vparamss: List[List[ValDef]], rhs: Tree = EmptyTree)(using Context): DefDef =
568571
DefDef(nme.CONSTRUCTOR, joinParams(tparams, vparamss), TypeTree(), rhs)

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ object CheckCaptures:
130130
|A classifier class is a class extending `caps.Capability` and directly extending `caps.Classifier`.""",
131131
ann.srcPos)
132132
check(ref)
133-
case tpe =>
134-
report.error(em"$elem: $tpe is not a legal element of a capture set", ann.srcPos)
133+
case elem =>
134+
report.error(em"$elem is not a legal element of a capture set", ann.srcPos)
135135
ann.retainedSet.retainedElementsRaw.foreach(check)
136136

137137
/** Disallow bad roots anywhere in type `tp``.
@@ -840,6 +840,8 @@ class CheckCaptures extends Recheck, SymTransformer:
840840
appType match
841841
case appType @ CapturingType(appType1, refs)
842842
if qualType.exists
843+
&& !qualType.isBoxedCapturing
844+
&& !resultType.isBoxedCapturing
843845
&& !tree.fun.symbol.isConstructor
844846
&& !resultType.captureSet.containsResultCapability
845847
&& qualCaptures.mightSubcapture(refs)

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,7 @@ object StdNames {
573573
val ofDim: N = "ofDim"
574574
val on: N = "on"
575575
val only: N = "only"
576+
val onlyCapability: N = "onlyCapability"
576577
val opaque: N = "opaque"
577578
val open: N = "open"
578579
val ordinal: N = "ordinal"
@@ -591,6 +592,8 @@ object StdNames {
591592
val productPrefix: N = "productPrefix"
592593
val quotes : N = "quotes"
593594
val raw_ : N = "raw"
595+
val reachCapability: N = "reachCapability"
596+
val readOnlyCapability: N = "readOnlyCapability"
594597
val rd: N = "rd"
595598
val refl: N = "refl"
596599
val reflect: N = "reflect"

library/src/scala/collection/immutable/LazyListIterable.scala

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,13 +1264,26 @@ object LazyListIterable extends IterableFactory[LazyListIterable] {
12641264
if (it.hasNext) eagerCons(it.next(), newLL(eagerHeadFromIterator(it)))
12651265
else Empty
12661266

1267+
// TODO This should be (xss: (collection.Iterable[A]^)*)
12671268
override def concat[A](xss: collection.Iterable[A]*): LazyListIterable[A] =
12681269
if (xss.knownSize == 0) empty
12691270
else newLL(eagerHeadConcatIterators(xss.iterator))
12701271

1271-
private def eagerHeadConcatIterators[A](it: Iterator[collection.Iterable[A]^]^): LazyListIterable[A]^{it} =
1272-
if (!it.hasNext) Empty
1273-
else eagerHeadPrependIterator(it.next().iterator)(eagerHeadConcatIterators(it))
1272+
/* TODO This should be:
1273+
private def eagerHeadConcatIterators[A](it: Iterator[collection.Iterable[A]^]^): LazyListIterable[A]^{it*} =
1274+
if !it.hasNext then Empty
1275+
else
1276+
eagerHeadPrependIterator
1277+
(caps.unsafe.unsafeDiscardUses(it.next()).iterator)
1278+
(eagerHeadConcatIterators(it))
1279+
*/
1280+
1281+
private def eagerHeadConcatIterators[A](it: Iterator[collection.Iterable[A]]^): LazyListIterable[A]^{it} =
1282+
if !it.hasNext then Empty
1283+
else
1284+
eagerHeadPrependIterator
1285+
(it.next().iterator)
1286+
(eagerHeadConcatIterators(it))
12741287

12751288
/** An infinite LazyListIterable that repeatedly applies a given function to a start value.
12761289
*
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/apply-rule.scala:3:19 ------------------------------------
2+
3 | val _: () -> A = x // error
3+
| ^
4+
| Found: (x : () ->{s*} A^{})
5+
| Required: () -> A
6+
|
7+
| Note that capability s* is not included in capture set {}.
8+
|
9+
| longer explanation available when compiling with `-explain`
10+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/apply-rule.scala:5:19 ------------------------------------
11+
5 | val _: () -> A = y // error
12+
| ^
13+
| Found: (y : () ->{s*} A^{})
14+
| Required: () -> A
15+
|
16+
| Note that capability s* is not included in capture set {}.
17+
|
18+
| longer explanation available when compiling with `-explain`
19+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/apply-rule.scala:9:19 ------------------------------------
20+
9 | val _: () -> A = x // error
21+
| ^
22+
| Found: (x : () ->{C} A^{})
23+
| Required: () -> A
24+
|
25+
| Note that capability C is not included in capture set {}.
26+
|
27+
| longer explanation available when compiling with `-explain`
28+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/apply-rule.scala:11:19 -----------------------------------
29+
11 | val _: () -> A = y // error
30+
| ^
31+
| Found: (y : () ->{C} A^{})
32+
| Required: () -> A
33+
|
34+
| Note that capability C is not included in capture set {}.
35+
|
36+
| longer explanation available when compiling with `-explain`
37+
-- Error: tests/neg-custom-args/captures/apply-rule.scala:2:11 ---------------------------------------------------------
38+
2 | val x = s(0) // error
39+
| ^^^^
40+
| Local reach capability s* leaks into capture scope of method select.
41+
| You could try to abstract the capabilities referred to by s* in a capset variable.
42+
-- Error: tests/neg-custom-args/captures/apply-rule.scala:4:12 ---------------------------------------------------------
43+
4 | val y = s.head // error
44+
| ^^^^^^
45+
| Local reach capability s* leaks into capture scope of method select.
46+
| You could try to abstract the capabilities referred to by s* in a capset variable.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
def select[A](s: Seq[() => A]) =
2+
val x = s(0) // error
3+
val _: () -> A = x // error
4+
val y = s.head // error
5+
val _: () -> A = y // error
6+
7+
def select2[A, C^](s: Seq[() ->{C} A]) =
8+
val x = s(0)
9+
val _: () -> A = x // error
10+
val y = s.head
11+
val _: () -> A = y // error
12+
13+
14+
15+
16+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/nicolas1.scala:10:2 --------------------------------------
2+
10 | val all: Seq[Rand ?->{head, tail*} A] = head +: tail // error
3+
| ^
4+
| Found: (contextual$1: Rand^'s1) ?->{head, tail*} A^'s2
5+
| Required: (Rand^) ?->{head} A
6+
|
7+
| Note that capability tail* is not included in capture set {head}.
8+
|
9+
| where: ^ refers to the universal root capability
10+
11 | all(nextInt(all.length))
11+
|
12+
| longer explanation available when compiling with `-explain`
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import caps.*
2+
3+
trait Rand extends SharedCapability:
4+
def range(min: Int, max: Int): Int
5+
6+
def nextInt(max: Int): Rand ?-> Int =
7+
r ?=> r.range(0, max)
8+
9+
def oneOf[A](head: Rand ?=> A, tail: (Rand ?=> A)*): Rand ?->{head} A =
10+
val all: Seq[Rand ?->{head, tail*} A] = head +: tail // error
11+
all(nextInt(all.length))

0 commit comments

Comments
 (0)