diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index 07ff3b841283..21c58385b58a 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -95,12 +95,13 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: def apply(x: Boolean, tp: Type): Boolean = if x then true else if tp.derivesFromCapability && variance >= 0 then true - else tp match + else tp.dealiasKeepAnnots match case AnnotatedType(_, ann) if ann.symbol.isRetains && variance >= 0 => true case t: TypeRef if t.symbol.isAbstractOrParamType && !seen.contains(t.symbol) => seen += t.symbol apply(x, t.info.bounds.hi) - case _ => foldOver(x, tp) + case tp1 => + foldOver(x, tp1) def apply(tp: Type): Boolean = apply(false, tp) if symd.symbol.isRefiningParamAccessor @@ -270,6 +271,8 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: def mapInferred(refine: Boolean): TypeMap = new TypeMap with SetupTypeMap: override def toString = "map inferred" + var refiningNames: Set[Name] = Set() + /** Refine a possibly applied class type C where the class has tracked parameters * x_1: T_1, ..., x_n: T_n to C { val x_1: T_1^{CV_1}, ..., val x_n: T_n^{CV_n} } * where CV_1, ..., CV_n are fresh capture set variables. @@ -282,7 +285,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: cls.paramGetters.foldLeft(tp) { (core, getter) => if atPhase(thisPhase.next)(getter.hasTrackedParts) && getter.isRefiningParamAccessor - && !getter.is(Tracked) + && !refiningNames.contains(getter.name) // Don't add a refinement if we have already an explicit one for the same name then val getterType = mapInferred(refine = false)(tp.memberInfo(getter)).strippedDealias @@ -306,6 +309,11 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: tp.derivedLambdaType( paramInfos = tp.paramInfos.mapConserve(_.dropAllRetains.bounds), resType = this(tp.resType)) + case tp @ RefinedType(parent, rname, rinfo) => + val saved = refiningNames + refiningNames += rname + val parent1 = try this(parent) finally refiningNames = saved + tp.derivedRefinedType(parent1, rname, this(rinfo)) case _ => mapFollowingAliases(tp) addVar( diff --git a/tests/neg-custom-args/captures/leaky.check b/tests/neg-custom-args/captures/leaky.check new file mode 100644 index 000000000000..b5ced771e0f6 --- /dev/null +++ b/tests/neg-custom-args/captures/leaky.check @@ -0,0 +1,32 @@ +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/leaky.scala:14:2 ----------------------------------------- +14 | val f: Any ->{a} Any = _ => // error + | ^ + | Found: test.runnable.Transform{val fun: Any ->{a} Any}^{a} + | Required: test.runnable.Transform +15 | a.print() +16 | () +17 | Transform(f) + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/leaky.scala:20:2 ----------------------------------------- +20 | val f: Any ->{a} Any = _ => // error + | ^ + | Found: test.runnable.Transform{val fun: Any ->{a} Any}^{a} + | Required: test.runnable.Transform +21 | a.print() +22 | () +23 | val x = Transform(f) +24 | x + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/leaky.scala:27:2 ----------------------------------------- +27 | val f: Any ->{a} Any = _ => // error + | ^ + | Found: test.runnable.Transform{val fun: Any ->{a} Any}^{a} + | Required: test.runnable.Transform +28 | a.print() +29 | () +30 | val x = Transform.app(f) +31 | x + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/leaky.scala b/tests/neg-custom-args/captures/leaky.scala new file mode 100644 index 000000000000..b401cab0df63 --- /dev/null +++ b/tests/neg-custom-args/captures/leaky.scala @@ -0,0 +1,37 @@ +package test.runnable +import language.experimental.captureChecking + +case class A() extends caps.Capability: + def print() = println("leaking...") + +class Transform(fun: Any => Any): + def run() = fun(()) +object Transform: + def app(f: Any => Any): Transform { val fun: Any->{f} Any } ^ {f} = + Transform(f) + +def leak(a: A): Transform^{} = + val f: Any ->{a} Any = _ => // error + a.print() + () + Transform(f) + +def leak1(a: A): Transform^{} = + val f: Any ->{a} Any = _ => // error + a.print() + () + val x = Transform(f) + x + +def leak2(a: A): Transform^{} = + val f: Any ->{a} Any = _ => // error + a.print() + () + val x = Transform.app(f) + x + +def withA[T](body: A => T): T = body(A()) + +@main def Main() = + val t = withA(leak) + t.run()