Skip to content

Commit 4d81484

Browse files
committed
Fix setup of class constructors
Fox the containsCovarRetains criterion that decides when to make a parameter not provate for the purposes of capture checking. We were missing a dealias, so `A => B` was not detected to contain a capture set. Also avoid refining with the same name twice in inferred types.
1 parent 2f32850 commit 4d81484

File tree

3 files changed

+82
-4
lines changed

3 files changed

+82
-4
lines changed

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,19 +95,21 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
9595
def apply(x: Boolean, tp: Type): Boolean =
9696
if x then true
9797
else if tp.derivesFromCapability && variance >= 0 then true
98-
else tp match
98+
else tp.dealiasKeepAnnots match
9999
case AnnotatedType(_, ann) if ann.symbol.isRetains && variance >= 0 => true
100100
case t: TypeRef if t.symbol.isAbstractOrParamType && !seen.contains(t.symbol) =>
101101
seen += t.symbol
102102
apply(x, t.info.bounds.hi)
103-
case _ => foldOver(x, tp)
103+
case tp1 =>
104+
foldOver(x, tp1)
104105
def apply(tp: Type): Boolean = apply(false, tp)
105106

106107
if symd.symbol.isRefiningParamAccessor
107108
&& symd.is(Private)
108109
&& symd.owner.is(CaptureChecked)
109110
&& containsCovarRetains(symd.symbol.originDenotation.info)
110-
then symd.flags &~ Private
111+
then
112+
symd.flags &~ Private
111113
else symd.flags
112114
end newFlagsFor
113115

@@ -270,6 +272,8 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
270272
def mapInferred(refine: Boolean): TypeMap = new TypeMap with SetupTypeMap:
271273
override def toString = "map inferred"
272274

275+
var refiningNames: Set[Name] = Set()
276+
273277
/** Refine a possibly applied class type C where the class has tracked parameters
274278
* x_1: T_1, ..., x_n: T_n to C { val x_1: T_1^{CV_1}, ..., val x_n: T_n^{CV_n} }
275279
* where CV_1, ..., CV_n are fresh capture set variables.
@@ -282,7 +286,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
282286
cls.paramGetters.foldLeft(tp) { (core, getter) =>
283287
if atPhase(thisPhase.next)(getter.hasTrackedParts)
284288
&& getter.isRefiningParamAccessor
285-
&& !getter.is(Tracked)
289+
&& !refiningNames.contains(getter.name) // Don't add a refinement if we have already an explicit one for the same name
286290
then
287291
val getterType =
288292
mapInferred(refine = false)(tp.memberInfo(getter)).strippedDealias
@@ -306,6 +310,11 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
306310
tp.derivedLambdaType(
307311
paramInfos = tp.paramInfos.mapConserve(_.dropAllRetains.bounds),
308312
resType = this(tp.resType))
313+
case tp @ RefinedType(parent, rname, rinfo) =>
314+
val saved = refiningNames
315+
refiningNames += rname
316+
val parent1 = try this(parent) finally refiningNames = saved
317+
tp.derivedRefinedType(parent1, rname, this(rinfo))
309318
case _ =>
310319
mapFollowingAliases(tp)
311320
addVar(
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/leaky.scala:14:2 -----------------------------------------
2+
14 | val f: Any ->{a} Any = _ => // error
3+
| ^
4+
| Found: test.runnable.Transform{val fun: Any ->{a} Any}^{a}
5+
| Required: test.runnable.Transform
6+
15 | a.print()
7+
16 | ()
8+
17 | Transform(f)
9+
|
10+
| longer explanation available when compiling with `-explain`
11+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/leaky.scala:20:2 -----------------------------------------
12+
20 | val f: Any ->{a} Any = _ => // error
13+
| ^
14+
| Found: test.runnable.Transform{val fun: Any ->{a} Any}^{a}
15+
| Required: test.runnable.Transform
16+
21 | a.print()
17+
22 | ()
18+
23 | val x = Transform(f)
19+
24 | x
20+
|
21+
| longer explanation available when compiling with `-explain`
22+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/leaky.scala:27:2 -----------------------------------------
23+
27 | val f: Any ->{a} Any = _ => // error
24+
| ^
25+
| Found: test.runnable.Transform{val fun: Any ->{a} Any}^{a}
26+
| Required: test.runnable.Transform
27+
28 | a.print()
28+
29 | ()
29+
30 | val x = Transform.app(f)
30+
31 | x
31+
|
32+
| longer explanation available when compiling with `-explain`
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package test.runnable
2+
import language.experimental.captureChecking
3+
4+
case class A() extends caps.Capability:
5+
def print() = println("leaking...")
6+
7+
class Transform(fun: Any => Any):
8+
def run() = fun(())
9+
object Transform:
10+
def app(f: Any => Any): Transform { val fun: Any->{f} Any } ^ {f} =
11+
Transform(f)
12+
13+
def leak(a: A): Transform^{} =
14+
val f: Any ->{a} Any = _ => // error
15+
a.print()
16+
()
17+
Transform(f)
18+
19+
def leak1(a: A): Transform^{} =
20+
val f: Any ->{a} Any = _ => // error
21+
a.print()
22+
()
23+
val x = Transform(f)
24+
x
25+
26+
def leak2(a: A): Transform^{} =
27+
val f: Any ->{a} Any = _ => // error
28+
a.print()
29+
()
30+
val x = Transform.app(f)
31+
x
32+
33+
def withA[T](body: A => T): T = body(A())
34+
35+
@main def Main() =
36+
val t = withA(leak)
37+
t.run()

0 commit comments

Comments
 (0)