Skip to content

Commit b1aac05

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 8f2c713 commit b1aac05

File tree

8 files changed

+104
-9
lines changed

8 files changed

+104
-9
lines changed

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,9 @@ 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)
527529
def scalaUnit(implicit src: SourceFile): Select = scalaDot(tpnme.Unit)
528530
def scalaAny(implicit src: SourceFile): Select = scalaDot(tpnme.Any)
529531

@@ -553,16 +555,16 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
553555
Annotated(parent, annot)
554556

555557
def makeReachAnnot()(using Context): Tree =
556-
New(ref(defn.ReachCapabilityAnnot.typeRef), Nil :: Nil)
558+
New(scalaAnnotationInternalDot(tpnme.reachCapability), Nil)
557559

558560
def makeReadOnlyAnnot()(using Context): Tree =
559-
New(ref(defn.ReadOnlyCapabilityAnnot.typeRef), Nil :: Nil)
561+
New(scalaAnnotationInternalDot(tpnme.readOnlyCapability), Nil)
560562

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

564566
def makeConsumeAnnot()(using Context): Tree =
565-
New(ref(defn.ConsumeAnnot.typeRef), Nil :: Nil)
567+
New(scalaCapsDot(nme.consume), Nil :: Nil)
566568

567569
def makeConstructor(tparams: List[TypeDef], vparamss: List[List[ValDef]], rhs: Tree = EmptyTree)(using Context): DefDef =
568570
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: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,9 +1268,12 @@ object LazyListIterable extends IterableFactory[LazyListIterable] {
12681268
if (xss.knownSize == 0) empty
12691269
else newLL(eagerHeadConcatIterators(xss.iterator))
12701270

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))
1271+
private def eagerHeadConcatIterators[A](it: Iterator[collection.Iterable[A]^]^): LazyListIterable[A]^{it*} =
1272+
if !it.hasNext then Empty
1273+
else
1274+
eagerHeadPrependIterator
1275+
(caps.unsafe.unsafeDiscardUses(it.next()).iterator)
1276+
(eagerHeadConcatIterators(it))
12741277

12751278
/** An infinite LazyListIterable that repeatedly applies a given function to a start value.
12761279
*
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)